summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/cng.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/qa.gitlab-ci.yml11
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml117
-rw-r--r--.rubocop.yml1
-rw-r--r--CHANGELOG.md111
-rw-r--r--Dangerfile2
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile14
-rw-r--r--Gemfile.lock115
-rw-r--r--app/assets/javascripts/api.js7
-rw-r--r--app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js2
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.vue2
-rw-r--r--app/assets/javascripts/boards/models/list.js1
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js9
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue16
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js10
-rw-r--r--app/assets/javascripts/gl_form.js4
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/message_field.vue1
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/upload.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue8
-rw-r--r--app/assets/javascripts/ide/constants.js7
-rw-r--r--app/assets/javascripts/ide/ide_router.js10
-rw-r--r--app/assets/javascripts/ide/lib/files.js4
-rw-r--r--app/assets/javascripts/ide/stores/actions.js6
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/actions.js25
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/mutations.js3
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js4
-rw-r--r--app/assets/javascripts/ide/stores/utils.js14
-rw-r--r--app/assets/javascripts/issuable_form.js2
-rw-r--r--app/assets/javascripts/issue_show/components/description.vue1
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue1
-rw-r--r--app/assets/javascripts/issue_show/components/fields/title.vue1
-rw-r--r--app/assets/javascripts/issue_show/components/title.vue1
-rw-r--r--app/assets/javascripts/lib/graphql.js14
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js2
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue1
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle.js2
-rw-r--r--app/assets/javascripts/mr_popover/index.js15
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue1
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue1
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue1
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue7
-rw-r--r--app/assets/javascripts/pages/profiles/show/index.js2
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue11
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js13
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue22
-rw-r--r--app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue21
-rw-r--r--app/assets/stylesheets/components/popover.scss4
-rw-r--r--app/assets/stylesheets/components/related_items_list.scss97
-rw-r--r--app/assets/stylesheets/components/toast.scss3
-rw-r--r--app/assets/stylesheets/pages/boards.scss4
-rw-r--r--app/assets/stylesheets/pages/issuable.scss1
-rw-r--r--app/assets/stylesheets/pages/labels.scss4
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb7
-rw-r--r--app/controllers/concerns/milestone_actions.rb2
-rw-r--r--app/controllers/projects/branches_controller.rb8
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb2
-rw-r--r--app/controllers/projects/wikis_controller.rb4
-rw-r--r--app/controllers/sessions_controller.rb12
-rw-r--r--app/finders/issuable_finder.rb1
-rw-r--r--app/helpers/broadcast_messages_helper.rb2
-rw-r--r--app/helpers/labels_helper.rb48
-rw-r--r--app/helpers/markup_helper.rb3
-rw-r--r--app/models/ci/job_artifact.rb2
-rw-r--r--app/models/ci/pipeline.rb1
-rw-r--r--app/models/clusters/platforms/kubernetes.rb2
-rw-r--r--app/models/concerns/cache_markdown_field.rb2
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb10
-rw-r--r--app/models/concerns/update_project_statistics.rb4
-rw-r--r--app/models/global_label.rb6
-rw-r--r--app/models/global_milestone.rb4
-rw-r--r--app/models/group.rb4
-rw-r--r--app/models/internal_id.rb8
-rw-r--r--app/models/label.rb5
-rw-r--r--app/models/lfs_object.rb2
-rw-r--r--app/models/members/group_member.rb2
-rw-r--r--app/models/members/project_member.rb2
-rw-r--r--app/models/merge_request.rb2
-rw-r--r--app/models/namespace.rb6
-rw-r--r--app/models/pages_domain.rb12
-rw-r--r--app/models/project.rb6
-rw-r--r--app/models/project_wiki.rb22
-rw-r--r--app/models/remote_mirror.rb2
-rw-r--r--app/models/route.rb16
-rw-r--r--app/models/user.rb2
-rw-r--r--app/presenters/label_presenter.rb43
-rw-r--r--app/services/compare_service.rb4
-rw-r--r--app/services/delete_branch_service.rb24
-rw-r--r--app/services/projects/propagate_service_template.rb2
-rw-r--r--app/services/service_response.rb31
-rw-r--r--app/services/test_hooks/project_service.rb2
-rw-r--r--app/views/admin/broadcast_messages/_form.html.haml1
-rw-r--r--app/views/admin/labels/_label.html.haml2
-rw-r--r--app/views/events/event/_common.html.haml3
-rw-r--r--app/views/events/event/_note.html.haml3
-rw-r--r--app/views/groups/labels/index.html.haml7
-rw-r--r--app/views/import/gitea/new.html.haml6
-rw-r--r--app/views/import/gitea/status.html.haml2
-rw-r--r--app/views/projects/_home_panel.html.haml2
-rw-r--r--app/views/projects/_import_project_pane.html.haml2
-rw-r--r--app/views/projects/_zen.html.haml1
-rw-r--r--app/views/projects/forks/error.html.haml14
-rw-r--r--app/views/projects/forks/index.html.haml13
-rw-r--r--app/views/projects/forks/new.html.haml16
-rw-r--r--app/views/projects/issues/_issue.html.haml6
-rw-r--r--app/views/projects/issues/new.html.haml9
-rw-r--r--app/views/projects/issues/show.html.haml6
-rw-r--r--app/views/projects/labels/index.html.haml4
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml4
-rw-r--r--app/views/projects/merge_requests/show.html.haml8
-rw-r--r--app/views/projects/mirrors/_mirror_repos.html.haml4
-rw-r--r--app/views/search/_results.html.haml5
-rw-r--r--app/views/shared/_delete_label_modal.html.haml4
-rw-r--r--app/views/shared/_label.html.haml8
-rw-r--r--app/views/shared/_label_row.html.haml5
-rw-r--r--app/views/shared/_sidebar_toggle_button.html.haml4
-rw-r--r--app/views/shared/icons/_gitea_logo.svg.erb1
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/views/shared/issuable/form/_title.html.haml2
-rw-r--r--app/views/shared/milestones/_issuable.html.haml2
-rw-r--r--changelogs/unreleased/10327-enable-reliable-fetcher-by-default.yml5
-rw-r--r--changelogs/unreleased/11254-overflow-ce.yml5
-rw-r--r--changelogs/unreleased/27424-tklk-gitea-logo.yml5
-rw-r--r--changelogs/unreleased/47584-label-text-color.yml5
-rw-r--r--changelogs/unreleased/48479-auto-direction-for-issue-title.yml5
-rw-r--r--changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml5
-rw-r--r--changelogs/unreleased/57017-add-toast-success-message.yml5
-rw-r--r--changelogs/unreleased/57293-fix-image-rename.yml5
-rw-r--r--changelogs/unreleased/58252-web-ide-dropdown-duplicates.yml5
-rw-r--r--changelogs/unreleased/58850-fix-misplaced-swipe-view-26969.yml5
-rw-r--r--changelogs/unreleased/59514-uploading-images-base64.yml5
-rw-r--r--changelogs/unreleased/60387-use-icons-in-user-popovers.yml5
-rw-r--r--changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml5
-rw-r--r--changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml5
-rw-r--r--changelogs/unreleased/60552-period-dropdown.yml5
-rw-r--r--changelogs/unreleased/60687-enviro-dropdown.yml5
-rw-r--r--changelogs/unreleased/60906-fix-wiki-links.yml5
-rw-r--r--changelogs/unreleased/add-ci-variable-protected-ref.yml5
-rw-r--r--changelogs/unreleased/autodevops_remote_private_helm_repository.yml6
-rw-r--r--changelogs/unreleased/ce-remove-already-signed-in.yml5
-rw-r--r--changelogs/unreleased/fix-api-ide-relative-url-root.yml5
-rw-r--r--changelogs/unreleased/fix-lazy-blobs-requesting-all-previous-blobs.yml6
-rw-r--r--changelogs/unreleased/fj-53523-add-option-avoid-loading-wiki-page-content.yml5
-rw-r--r--changelogs/unreleased/fj-60827-fix-web-strategy-error.yml5
-rw-r--r--changelogs/unreleased/jc-update-list-last-commits.yml5
-rw-r--r--changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml5
-rw-r--r--changelogs/unreleased/rails5-1.yml5
-rw-r--r--changelogs/unreleased/refactor-58829-migrate-notes-spec-to-jest.yml5
-rw-r--r--changelogs/unreleased/sh-avoid-fetching-temp-refs-within-project.yml5
-rw-r--r--changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml5
-rw-r--r--changelogs/unreleased/sh-bump-ruby-required-version-check.yml5
-rw-r--r--changelogs/unreleased/sh-disable-internal-ids-available-check.yml5
-rw-r--r--changelogs/unreleased/sh-fix-autocomplete-mirror-repo.yml5
-rw-r--r--changelogs/unreleased/sh-upgrade-grpc-and-protobuf.yml5
-rw-r--r--changelogs/unreleased/support-negative-matches.yml5
-rw-r--r--changelogs/unreleased/update-workhorse-master.yml5
-rw-r--r--config/application.rb3
-rw-r--r--config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb94
-rw-r--r--config/initializers/active_record_build_select.rb24
-rw-r--r--config/initializers/active_record_locking.rb76
-rw-r--r--config/initializers/ar_native_database_types.rb3
-rw-r--r--config/initializers/postgresql_opclasses_support.rb2
-rw-r--r--config/initializers/sidekiq.rb17
-rw-r--r--danger/ce_ee_vue_templates/Dangerfile56
-rw-r--r--db/fixtures/development/09_issues.rb20
-rw-r--r--db/migrate/20161031181638_add_unique_index_to_subscriptions.rb4
-rw-r--r--db/migrate/20190322164830_add_auto_ssl_enabled_to_pages_domain.rb17
-rw-r--r--db/schema.rb289
-rw-r--r--doc/ci/large_repositories/index.md39
-rw-r--r--doc/ci/variables/README.md15
-rw-r--r--doc/ci/variables/predefined_variables.md35
-rw-r--r--doc/development/contributing/issue_workflow.md5
-rw-r--r--doc/development/documentation/styleguide.md9
-rw-r--r--doc/development/i18n/externalization.md2
-rw-r--r--doc/development/i18n/proofreader.md14
-rw-r--r--doc/development/rake_tasks.md16
-rw-r--r--doc/development/testing_guide/img/review_apps_cicd_architecture.pngbin73240 -> 136431 bytes
-rw-r--r--doc/development/testing_guide/review_apps.md162
-rw-r--r--doc/topics/autodevops/index.md3
-rw-r--r--doc/user/project/integrations/gitlab_slack_application.md65
-rw-r--r--doc/user/project/integrations/img/gitlab_slack_app_landing_page.pngbin0 -> 32992 bytes
-rw-r--r--doc/user/project/integrations/project_services.md6
-rw-r--r--doc/user/project/integrations/slack_slash_commands.md13
-rw-r--r--doc/user/project/pipelines/job_artifacts.md3
-rw-r--r--doc/user/project/repository/index.md33
-rw-r--r--lib/api/branches.rb4
-rw-r--r--lib/api/helpers.rb4
-rw-r--r--lib/api/namespaces.rb2
-rw-r--r--lib/api/wikis.rb3
-rw-r--r--lib/banzai/filter/label_reference_filter.rb6
-rw-r--r--lib/gitlab/checks/branch_check.rb6
-rw-r--r--lib/gitlab/checks/lfs_check.rb1
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb28
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb31
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexer.rb4
-rw-r--r--lib/gitlab/ci/pipeline/expression/statement.rb13
-rw-r--r--lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml2
-rw-r--r--lib/gitlab/git/repository.rb32
-rw-r--r--lib/gitlab/git/wiki.rb24
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb4
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/wiki_service.rb23
-rw-r--r--lib/gitlab/graphql/authorize/authorize_field_service.rb6
-rw-r--r--lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb5
-rw-r--r--lib/gitlab/performance_bar/peek_query_tracker.rb6
-rw-r--r--lib/gitlab/search_results.rb6
-rw-r--r--lib/quality/seeders/issues.rb58
-rw-r--r--lib/tasks/gitlab/seed.rake34
-rw-r--r--locale/gitlab.pot45
-rw-r--r--package.json2
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock4
-rwxr-xr-xscripts/review_apps/review-apps.sh97
-rw-r--r--scripts/utils.sh136
-rw-r--r--spec/controllers/concerns/issuable_collections_spec.rb110
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb6
-rw-r--r--spec/controllers/projects/compare_controller_spec.rb2
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb9
-rw-r--r--spec/controllers/projects/wikis_controller_spec.rb22
-rw-r--r--spec/controllers/search_controller_spec.rb24
-rw-r--r--spec/features/admin/admin_users_spec.rb7
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb20
-rw-r--r--spec/features/issuables/markdown_references/jira_spec.rb2
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb114
-rw-r--r--spec/features/users/login_spec.rb16
-rw-r--r--spec/fixtures/api/schemas/entities/issue.json2
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_basic.json2
-rw-r--r--spec/frontend/clusters/components/application_row_spec.js23
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js34
-rw-r--r--spec/frontend/ide/stores/modules/file_templates/mutations_spec.js41
-rw-r--r--spec/frontend/mr_popover/index_spec.js20
-rw-r--r--spec/frontend/vue_shared/components/notes/placeholder_note_spec.js (renamed from spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js)2
-rw-r--r--spec/frontend/vue_shared/components/notes/placeholder_system_note_spec.js (renamed from spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js)2
-rw-r--r--spec/frontend/vue_shared/components/notes/system_note_spec.js (renamed from spec/javascripts/vue_shared/components/notes/system_note_spec.js)0
-rw-r--r--spec/helpers/labels_helper_spec.rb48
-rw-r--r--spec/javascripts/api_spec.js14
-rw-r--r--spec/javascripts/boards/list_spec.js3
-rw-r--r--spec/javascripts/boards/mock_data.js1
-rw-r--r--spec/javascripts/ide/components/new_dropdown/upload_spec.js4
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js5
-rw-r--r--spec/javascripts/ide/stores/modules/file_templates/actions_spec.js54
-rw-r--r--spec/javascripts/ide/stores/utils_spec.js19
-rw-r--r--spec/javascripts/merge_request_spec.js2
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js3
-rw-r--r--spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js16
-rw-r--r--spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js18
-rw-r--r--spec/lib/api/helpers_spec.rb12
-rw-r--r--spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb2
-rw-r--r--spec/lib/gitlab/checks/branch_check_spec.rb118
-rw-r--r--spec/lib/gitlab/checks/lfs_check_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb80
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb12
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb2
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb64
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb16
-rw-r--r--spec/lib/gitlab/gitaly_client/wiki_service_spec.rb6
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb16
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb12
-rw-r--r--spec/lib/gitlab/import_export/attribute_configuration_spec.rb2
-rw-r--r--spec/lib/quality/seeders/issues_spec.rb18
-rw-r--r--spec/models/blob_spec.rb15
-rw-r--r--spec/models/ci/bridge_spec.rb2
-rw-r--r--spec/models/ci/build_spec.rb3
-rw-r--r--spec/models/ci/pipeline_spec.rb3
-rw-r--r--spec/models/ci/runner_spec.rb5
-rw-r--r--spec/models/internal_id_spec.rb11
-rw-r--r--spec/models/project_wiki_spec.rb74
-rw-r--r--spec/models/repository_spec.rb19
-rw-r--r--spec/models/wiki_page_spec.rb66
-rw-r--r--spec/presenters/label_presenter_spec.rb65
-rw-r--r--spec/requests/api/runner_spec.rb2
-rw-r--r--spec/requests/rack_attack_global_spec.rb4
-rw-r--r--spec/services/compare_service_spec.rb13
-rw-r--r--spec/services/delete_branch_service_spec.rb24
-rw-r--r--spec/services/merge_requests/merge_to_ref_service_spec.rb2
-rw-r--r--spec/services/projects/update_service_spec.rb2
-rw-r--r--spec/services/service_response_spec.rb57
-rw-r--r--spec/services/users/destroy_service_spec.rb4
-rw-r--r--spec/services/users/migrate_to_ghost_user_service_spec.rb2
-rw-r--r--spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb6
-rw-r--r--spec/support/helpers/login_helpers.rb3
-rw-r--r--spec/support/helpers/query_recorder.rb2
-rw-r--r--spec/support/helpers/test_request_helpers.rb4
-rw-r--r--spec/support/services/migrate_to_ghost_user_service_shared_examples.rb4
-rw-r--r--yarn.lock14
294 files changed, 2945 insertions, 1555 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4d30efccb5c..f1573dba32a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -28,6 +28,8 @@ stages:
- prepare
- merge
- test
+ - review
+ - qa
- post-test
- pages
- post-cleanup
diff --git a/.gitlab/ci/cng.gitlab-ci.yml b/.gitlab/ci/cng.gitlab-ci.yml
index e15f8ed91e0..c384bcdcdfc 100644
--- a/.gitlab/ci/cng.gitlab-ci.yml
+++ b/.gitlab/ci/cng.gitlab-ci.yml
@@ -9,7 +9,7 @@ cloud-native-image:
cache: {}
when: manual
script:
- - gem install gitlab --no-document
+ - install_gitlab_gem
- CNG_PROJECT_PATH="gitlab-org/build/CNG" BUILD_TRIGGER_TOKEN=$CI_JOB_TOKEN ./scripts/trigger-build cng
only:
- tags@gitlab-org/gitlab-ce
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index fd179f77e26..bfefd42c52d 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -38,6 +38,10 @@ gitlab:assets:compile:
- bundle exec rake gitlab:assets:compile
- time scripts/build_assets_image
- scripts/clean-old-cached-assets
+ # Play dependent manual jobs
+ - install_api_client_dependencies_with_apt
+ - play_job "review-build-cng" || true # this job might not exist so ignore the failure if it cannot be played
+ - play_job "schedule:review-build-cng" || true # this job might not exist so ignore the failure if it cannot be played
artifacts:
name: webpack-report
expire_in: 31d
diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml
index 07b38c9aa85..85c6409186e 100644
--- a/.gitlab/ci/qa.gitlab-ci.yml
+++ b/.gitlab/ci/qa.gitlab-ci.yml
@@ -1,20 +1,17 @@
package-and-qa:
image: ruby:2.5-alpine
- stage: test
+ stage: qa
+ when: manual
before_script: []
dependencies: []
cache: {}
variables:
GIT_DEPTH: "1"
- API_TOKEN: "${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}"
retry: 0
script:
- - apk add --update openssl curl jq
- - gem install gitlab --no-document
- - source ./scripts/review_apps/review-apps.sh
- - wait_for_job_to_be_done "gitlab:assets:compile"
+ - source scripts/utils.sh
+ - install_gitlab_gem
- ./scripts/trigger-build omnibus
- when: manual
only:
- /.+/@gitlab-org/gitlab-ce
- /.+/@gitlab-org/gitlab-ee
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index 9cfb50eeefc..cc5d6060716 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -26,12 +26,10 @@
extends: .dedicated-runner
<<: *review-only
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
- stage: test
cache: {}
dependencies: []
- environment: &review-environment
- name: review/${CI_COMMIT_REF_NAME}
- url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}
+ before_script:
+ - source scripts/utils.sh
.review-docker: &review-docker
<<: *review-base
@@ -42,18 +40,13 @@
- gitlab-org
- docker
variables: &review-docker-variables
- GIT_DEPTH: "1"
DOCKER_DRIVER: overlay2
DOCKER_HOST: tcp://docker:2375
LATEST_QA_IMAGE: "gitlab/${CI_PROJECT_NAME}-qa:nightly"
QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab/${CI_PROJECT_NAME}-qa:${CI_COMMIT_REF_SLUG}"
- before_script: []
build-qa-image:
<<: *review-docker
- variables:
- <<: *review-docker-variables
- GIT_DEPTH: "20"
stage: prepare
script:
- time docker build --cache-from ${LATEST_QA_IMAGE} --tag ${QA_IMAGE} ./qa/
@@ -63,16 +56,14 @@ build-qa-image:
.review-build-cng-base: &review-build-cng-base
image: ruby:2.5-alpine
stage: test
- before_script: []
+ when: manual
+ before_script:
+ - source scripts/utils.sh
+ - install_api_client_dependencies_with_apk
+ - install_gitlab_gem
dependencies: []
cache: {}
- variables:
- API_TOKEN: "${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}"
script:
- - apk add --update openssl curl jq
- - gem install gitlab --no-document
- - source ./scripts/review_apps/review-apps.sh
- - wait_for_job_to_be_done "gitlab:assets:compile"
- BUILD_TRIGGER_TOKEN=$REVIEW_APPS_BUILD_TRIGGER_TOKEN ./scripts/trigger-build cng
review-build-cng:
@@ -85,26 +76,32 @@ schedule:review-build-cng:
.review-deploy-base: &review-deploy-base
<<: *review-base
+ stage: review
retry: 2
allow_failure: true
variables:
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
GITLAB_HELM_CHART_REF: "master"
- API_TOKEN: "${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}"
- environment:
- <<: *review-environment
+ environment: &review-environment
+ name: review/${CI_COMMIT_REF_NAME}
+ url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}
on_stop: review-stop
before_script:
- export GITLAB_SHELL_VERSION=$(<GITLAB_SHELL_VERSION)
- export GITALY_VERSION=$(<GITALY_SERVER_VERSION)
- export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION)
- - apk update && apk add jq
- - gem install gitlab --no-document
- - source ./scripts/review_apps/review-apps.sh
+ - echo "${CI_ENVIRONMENT_URL}" > review_app_url.txt
+ - source scripts/utils.sh
+ - install_api_client_dependencies_with_apk
+ - source scripts/review_apps/review-apps.sh
script:
- - wait_for_job_to_be_done "review-build-cng"
- perform_review_app_deployment
+ artifacts:
+ paths:
+ - review_app_url.txt
+ expire_in: 2 days
+ when: always
review-deploy:
<<: *review-deploy-base
@@ -113,15 +110,29 @@ schedule:review-deploy:
<<: *review-deploy-base
<<: *review-schedules-only
script:
- - wait_for_job_to_be_done "schedule:review-build-cng"
- perform_review_app_deployment
+review-stop:
+ <<: *review-base
+ stage: review
+ when: manual
+ allow_failure: true
+ variables:
+ GIT_DEPTH: "1"
+ environment:
+ <<: *review-environment
+ action: stop
+ script:
+ - source scripts/review_apps/review-apps.sh
+ - delete
+ - cleanup
+
.review-qa-base: &review-qa-base
<<: *review-docker
+ stage: qa
allow_failure: true
variables:
<<: *review-docker-variables
- API_TOKEN: "${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}"
QA_ARTIFACTS_DIR: "${CI_PROJECT_DIR}/qa"
QA_CAN_TEST_GIT_PROTOCOL_V2: "false"
GITLAB_USERNAME: "root"
@@ -131,40 +142,45 @@ schedule:review-deploy:
GITHUB_ACCESS_TOKEN: "${REVIEW_APPS_QA_GITHUB_ACCESS_TOKEN}"
EE_LICENSE: "${REVIEW_APPS_EE_LICENSE}"
QA_DEBUG: "true"
+ dependencies:
+ - review-deploy
artifacts:
paths:
- ./qa/gitlab-qa-run-*
expire_in: 7 days
when: always
before_script:
- - echo "${QA_IMAGE}"
+ - export CI_ENVIRONMENT_URL="$(cat review_app_url.txt)"
- echo "${CI_ENVIRONMENT_URL}"
- - apk update && apk add curl jq
- - source ./scripts/review_apps/review-apps.sh
+ - echo "${QA_IMAGE}"
+ - source scripts/utils.sh
+ - install_api_client_dependencies_with_apk
- gem install gitlab-qa --no-document ${GITLAB_QA_VERSION:+ --version ${GITLAB_QA_VERSION}}
review-qa-smoke:
<<: *review-qa-base
retry: 2
script:
- - wait_for_job_to_be_done "review-deploy"
- gitlab-qa Test::Instance::Smoke "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
review-qa-all:
<<: *review-qa-base
+ when: manual
script:
- - wait_for_job_to_be_done "review-deploy"
- gitlab-qa Test::Instance::Any "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
- when: manual
.review-performance-base: &review-performance-base
<<: *review-qa-base
- script:
- - wait_for_job_to_be_done "review-deploy"
+ stage: qa
+ before_script:
+ - export CI_ENVIRONMENT_URL="$(cat review_app_url.txt)"
+ - echo "${CI_ENVIRONMENT_URL}"
- mkdir -p gitlab-exporter
- wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
- - mkdir sitespeed-results
- - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL"
+ - mkdir -p sitespeed-results
+ script:
+ - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "${CI_ENVIRONMENT_URL}"
+ after_script:
- mv sitespeed-results/data/performance.json performance.json
artifacts:
paths:
@@ -175,42 +191,27 @@ review-qa-all:
review-performance:
<<: *review-performance-base
-review-stop:
- <<: *review-base
- extends: .single-script-job-dedicated-runner
- image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
- allow_failure: true
- variables:
- SCRIPT_NAME: "review_apps/review-apps.sh"
- when: manual
- environment:
- <<: *review-environment
- action: stop
- script:
- - source $(basename "${SCRIPT_NAME}")
- - delete
- - cleanup
+schedule:review-performance:
+ <<: *review-performance-base
+ <<: *review-schedules-only
+ dependencies:
+ - schedule:review-deploy
schedule:review-cleanup:
<<: *review-base
<<: *review-schedules-only
- stage: build
+ stage: review
allow_failure: true
variables:
GIT_DEPTH: "1"
environment:
name: review/auto-cleanup
before_script:
- - gem install gitlab --no-document
+ - source scripts/utils.sh
+ - install_gitlab_gem
script:
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb
-schedule:review-performance:
- <<: *review-performance-base
- <<: *review-schedules-only
- script:
- - wait_for_job_to_be_done "schedule:review-deploy"
-
danger-review:
extends: .dedicated-pull-cache-job
image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger
diff --git a/.rubocop.yml b/.rubocop.yml
index aa49f41ebf4..e5fe527e611 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -75,6 +75,7 @@ Naming/FileName:
- 'qa/spec/**/*'
- 'qa/qa/specs/**/*'
- 'qa/bin/*'
+ - 'ee/bin/*'
- 'config/**/*'
- 'ee/config/**/*'
- 'lib/generators/**/*'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c512585ff6a..bd4c0e479cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,26 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.10.1 (2019-04-23)
+
+### Fixed (2 changes)
+
+- Upgrade Gitaly to 1.34.0. !27494
+- Fix filtering of labels from system note link. !27507
+
+### Changed (1 change)
+
+- Disable just-in-time Kubernetes resource creation for project level clusters. !27352
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+### Other (1 change)
+
+- Bump required Ruby version check to 2.5.3. !27495
+
+
## 11.10.0 (2019-04-22)
### Security (9 changes)
@@ -233,6 +253,13 @@ entry.
- Removes EE differences for environment_item.vue.
+## 11.9.9 (2019-04-23)
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+
## 11.9.8 (2019-04-11)
### Deprecated (1 change)
@@ -584,6 +611,28 @@ entry.
- Removes EE differences for jobs/getters.js.
+## 11.8.8 (2019-04-23)
+
+### Fixed (5 changes)
+
+- Bring back Rugged implementation of find_commit. !25477
+- Fix bug in BitBucket imports with SHA shorter than 40 chars. !26050
+- Fix health checks not working behind load balancers. !26055
+- Fix error creating a merge request when diff includes a null byte. !26190
+- Avoid excessive recursive calls with Rugged TreeEntries. !26813
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+### Other (4 changes)
+
+- Bring back Rugged implementation of GetTreeEntries. !25674
+- Bring back Rugged implementation of CommitIsAncestor. !25702
+- Bring back Rugged implementation of TreeEntry. !25706
+- Bring back Rugged implementation of commit_tree_entry. !25896
+
+
## 11.8.3 (2019-03-19)
### Security (1 change)
@@ -853,6 +902,25 @@ entry.
- Creates mixin to reduce code duplication between CE and EE in graph component.
+## 11.7.12 (2019-04-23)
+
+### Fixed (2 changes)
+
+- Bring back Rugged implementation of find_commit. !25477
+- Avoid excessive recursive calls with Rugged TreeEntries. !26813
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+### Other (4 changes)
+
+- Bring back Rugged implementation of GetTreeEntries. !25674
+- Bring back Rugged implementation of CommitIsAncestor. !25702
+- Bring back Rugged implementation of TreeEntry. !25706
+- Bring back Rugged implementation of commit_tree_entry. !25896
+
+
## 11.7.11 (2019-04-09)
- No changes.
@@ -1128,6 +1196,30 @@ entry.
- Update url placeholder for the sentry configuration page. !24338
+## 11.6.11 (2019-04-23)
+
+### Security (2 changes)
+
+- Fixed ability to see private groups by users not belonging to given group.
+- Fix XSS in resolve conflicts form.
+
+### Fixed (2 changes)
+
+- Bring back Rugged implementation of find_commit. !25477
+- Avoid excessive recursive calls with Rugged TreeEntries. !26813
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+### Other (4 changes)
+
+- Bring back Rugged implementation of GetTreeEntries. !25674
+- Bring back Rugged implementation of CommitIsAncestor. !25702
+- Bring back Rugged implementation of TreeEntry. !25706
+- Bring back Rugged implementation of commit_tree_entry. !25896
+
+
## 11.6.10 (2019-02-28)
### Security (21 changes)
@@ -1499,6 +1591,25 @@ entry.
- Enable Rubocop on lib/gitlab. (gfyoung)
+## 11.5.11 (2019-04-23)
+
+### Fixed (2 changes)
+
+- Bring back Rugged implementation of find_commit. !25477
+- Avoid excessive recursive calls with Rugged TreeEntries. !26813
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+### Other (4 changes)
+
+- Bring back Rugged implementation of GetTreeEntries. !25674
+- Bring back Rugged implementation of CommitIsAncestor. !25702
+- Bring back Rugged implementation of TreeEntry. !25706
+- Bring back Rugged implementation of commit_tree_entry. !25896
+
+
## 11.5.8 (2019-01-28)
### Security (21 changes)
diff --git a/Dangerfile b/Dangerfile
index 3e8cb456003..9e3a08949b0 100644
--- a/Dangerfile
+++ b/Dangerfile
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
danger.import_plugin('danger/plugins/helper.rb')
unless helper.release_automation?
@@ -16,4 +17,5 @@ unless helper.release_automation?
danger.import_dangerfile(path: 'danger/roulette')
danger.import_dangerfile(path: 'danger/single_codebase')
danger.import_dangerfile(path: 'danger/gitlab_ui_wg')
+ danger.import_dangerfile(path: 'danger/ce_ee_vue_templates')
end
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 2b17ffd5042..a2d87226ac2 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-1.34.0
+1.35.0 \ No newline at end of file
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index acd405b1d62..df5119ec64e 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-8.6.0
+8.7.0
diff --git a/Gemfile b/Gemfile
index eaaf8125009..a350f194f62 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
source 'https://rubygems.org'
-gem 'rails', '5.0.7.2'
+gem 'rails', '5.1.7'
# Improves copy-on-write performance for MRI
gem 'nakayoshi_fork', '~> 0.0.4'
@@ -18,7 +18,7 @@ gem 'mysql2', '~> 0.4.10', group: :mysql
gem 'pg', '~> 1.1', group: :postgres
gem 'rugged', '~> 0.28'
-gem 'grape-path-helpers', '~> 1.0'
+gem 'grape-path-helpers', '~> 1.1'
gem 'faraday', '~> 0.12'
@@ -129,7 +129,7 @@ gem 'asciidoctor-plantuml', '0.0.8'
gem 'rouge', '~> 3.1'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0'
-gem 'nokogiri', '~> 1.10.1'
+gem 'nokogiri', '~> 1.10.3'
gem 'escape_utils', '~> 1.1'
# Calendar rendering
@@ -158,7 +158,7 @@ gem 'state_machines-activerecord', '~> 0.5.1'
gem 'acts-as-taggable-on', '~> 6.0'
# Background jobs
-gem 'sidekiq', '~> 5.2.1'
+gem 'sidekiq', '~> 5.2.7'
gem 'sidekiq-cron', '~> 1.0'
gem 'redis-namespace', '~> 1.6.0'
gem 'gitlab-sidekiq-fetcher', '~> 0.4.0', require: 'sidekiq-reliable-fetch'
@@ -416,11 +416,11 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 1.19.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 1.22.0', require: 'gitaly'
-gem 'grpc', '~> 1.15.0'
+gem 'grpc', '~> 1.19.0'
-gem 'google-protobuf', '~> 3.6'
+gem 'google-protobuf', '~> 3.7.1'
gem 'toml-rb', '~> 1.0.0', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index ba4418cd8b3..64f2f78a4f8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -4,41 +4,41 @@ GEM
RedCloth (4.3.2)
abstract_type (0.0.7)
ace-rails-ap (4.1.2)
- actioncable (5.0.7.2)
- actionpack (= 5.0.7.2)
- nio4r (>= 1.2, < 3.0)
+ actioncable (5.1.7)
+ actionpack (= 5.1.7)
+ nio4r (~> 2.0)
websocket-driver (~> 0.6.1)
- actionmailer (5.0.7.2)
- actionpack (= 5.0.7.2)
- actionview (= 5.0.7.2)
- activejob (= 5.0.7.2)
+ actionmailer (5.1.7)
+ actionpack (= 5.1.7)
+ actionview (= 5.1.7)
+ activejob (= 5.1.7)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
- actionpack (5.0.7.2)
- actionview (= 5.0.7.2)
- activesupport (= 5.0.7.2)
+ actionpack (5.1.7)
+ actionview (= 5.1.7)
+ activesupport (= 5.1.7)
rack (~> 2.0)
- rack-test (~> 0.6.3)
+ rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (5.0.7.2)
- activesupport (= 5.0.7.2)
+ actionview (5.1.7)
+ activesupport (= 5.1.7)
builder (~> 3.1)
- erubis (~> 2.7.0)
+ erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
- activejob (5.0.7.2)
- activesupport (= 5.0.7.2)
+ activejob (5.1.7)
+ activesupport (= 5.1.7)
globalid (>= 0.3.6)
- activemodel (5.0.7.2)
- activesupport (= 5.0.7.2)
- activerecord (5.0.7.2)
- activemodel (= 5.0.7.2)
- activesupport (= 5.0.7.2)
- arel (~> 7.0)
+ activemodel (5.1.7)
+ activesupport (= 5.1.7)
+ activerecord (5.1.7)
+ activemodel (= 5.1.7)
+ activesupport (= 5.1.7)
+ arel (~> 8.0)
activerecord_sane_schema_dumper (1.0)
rails (>= 5, < 6)
- activesupport (5.0.7.2)
+ activesupport (5.1.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@@ -52,7 +52,7 @@ GEM
public_suffix (>= 2.0.2, < 4.0)
aes_key_wrap (1.0.1)
akismet (2.0.0)
- arel (7.1.4)
+ arel (8.0.0)
asana (0.8.1)
faraday (~> 0.9)
faraday_middleware (~> 0.9)
@@ -185,8 +185,7 @@ GEM
mail (~> 2.7)
encryptor (3.0.0)
equalizer (0.0.11)
- erubi (1.7.1)
- erubis (2.7.0)
+ erubi (1.8.0)
escape_utils (1.2.1)
et-orbi (1.1.7)
tzinfo
@@ -257,8 +256,8 @@ GEM
fog-xml (0.1.3)
fog-core
nokogiri (>= 1.5.11, < 2.0.0)
- font-awesome-rails (4.7.0.1)
- railties (>= 3.2, < 5.1)
+ font-awesome-rails (4.7.0.4)
+ railties (>= 3.2, < 6.0)
foreman (0.84.0)
thor (~> 0.19.1)
formatador (0.2.5)
@@ -281,7 +280,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (1.19.0)
+ gitaly-proto (1.22.0)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-default_value_for (3.1.1)
@@ -317,8 +316,8 @@ GEM
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
- google-protobuf (3.6.1)
- googleapis-common-protos-types (1.0.3)
+ google-protobuf (3.7.1)
+ googleapis-common-protos-types (1.0.4)
google-protobuf (~> 3.0)
googleauth (0.6.6)
faraday (~> 0.12)
@@ -339,8 +338,8 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
- grape-path-helpers (1.0.6)
- activesupport (>= 4, < 5.1)
+ grape-path-helpers (1.1.0)
+ activesupport
grape (~> 1.0)
rake (~> 12)
grape_logging (1.7.0)
@@ -349,7 +348,7 @@ GEM
railties
sprockets-rails
graphql (1.8.1)
- grpc (1.15.0)
+ grpc (1.19.0)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
haml (5.0.4)
@@ -494,7 +493,7 @@ GEM
net-ssh (5.0.1)
netrc (0.11.0)
nio4r (2.3.1)
- nokogiri (1.10.2)
+ nokogiri (1.10.3)
mini_portile2 (~> 2.4.0)
nokogumbo (1.5.0)
nokogiri
@@ -658,19 +657,19 @@ GEM
rack
rack-proxy (0.6.0)
rack
- rack-test (0.6.3)
- rack (>= 1.0)
- rails (5.0.7.2)
- actioncable (= 5.0.7.2)
- actionmailer (= 5.0.7.2)
- actionpack (= 5.0.7.2)
- actionview (= 5.0.7.2)
- activejob (= 5.0.7.2)
- activemodel (= 5.0.7.2)
- activerecord (= 5.0.7.2)
- activesupport (= 5.0.7.2)
+ rack-test (1.1.0)
+ rack (>= 1.0, < 3)
+ rails (5.1.7)
+ actioncable (= 5.1.7)
+ actionmailer (= 5.1.7)
+ actionpack (= 5.1.7)
+ actionview (= 5.1.7)
+ activejob (= 5.1.7)
+ activemodel (= 5.1.7)
+ activerecord (= 5.1.7)
+ activesupport (= 5.1.7)
bundler (>= 1.3.0)
- railties (= 5.0.7.2)
+ railties (= 5.1.7)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.2)
actionpack (~> 5.x, >= 5.0.1)
@@ -684,9 +683,9 @@ GEM
rails-i18n (5.1.1)
i18n (>= 0.7, < 2)
railties (>= 5.0, < 6)
- railties (5.0.7.2)
- actionpack (= 5.0.7.2)
- activesupport (= 5.0.7.2)
+ railties (5.1.7)
+ actionpack (= 5.1.7)
+ activesupport (= 5.1.7)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
@@ -846,7 +845,7 @@ GEM
rack
shoulda-matchers (3.1.2)
activesupport (>= 4.0.0)
- sidekiq (5.2.5)
+ sidekiq (5.2.7)
connection_pool (~> 2.2, >= 2.2.2)
rack (>= 1.5.0)
rack-protection (>= 1.5.0)
@@ -1053,7 +1052,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 1.19.0)
+ gitaly-proto (~> 1.22.0)
github-markup (~> 1.7.0)
gitlab-default_value_for (~> 3.1.1)
gitlab-labkit (~> 0.1.2)
@@ -1063,15 +1062,15 @@ DEPENDENCIES
gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.2)
google-api-client (~> 0.23)
- google-protobuf (~> 3.6)
+ google-protobuf (~> 3.7.1)
gpgme (~> 2.0.18)
grape (~> 1.1.0)
grape-entity (~> 0.7.1)
- grape-path-helpers (~> 1.0)
+ grape-path-helpers (~> 1.1)
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (~> 1.8.0)
- grpc (~> 1.15.0)
+ grpc (~> 1.19.0)
haml_lint (~> 0.28.0)
hamlit (~> 2.8.8)
hangouts-chat (~> 0.0.5)
@@ -1104,7 +1103,7 @@ DEPENDENCIES
nakayoshi_fork (~> 0.0.4)
net-ldap
net-ssh (~> 5.0)
- nokogiri (~> 1.10.1)
+ nokogiri (~> 1.10.3)
oauth2 (~> 1.4)
octokit (~> 4.9)
omniauth (~> 1.8)
@@ -1142,7 +1141,7 @@ DEPENDENCIES
rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.9.3)
rack-proxy (~> 0.6.0)
- rails (= 5.0.7.2)
+ rails (= 5.1.7)
rails-controller-testing
rails-i18n (~> 5.1)
rainbow (~> 3.0)
@@ -1183,7 +1182,7 @@ DEPENDENCIES
settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6)
shoulda-matchers (~> 3.1.2)
- sidekiq (~> 5.2.1)
+ sidekiq (~> 5.2.7)
sidekiq-cron (~> 1.0)
simple_po_parser (~> 1.1.2)
simplecov (~> 0.14.0)
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 8754c253881..e583a8affd4 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -1,6 +1,7 @@
import $ from 'jquery';
import _ from 'underscore';
import axios from './lib/utils/axios_utils';
+import { joinPaths } from './lib/utils/url_utility';
const Api = {
groupsPath: '/api/:version/groups.json',
@@ -339,11 +340,7 @@ const Api = {
},
buildUrl(url) {
- let urlRoot = '';
- if (gon.relative_url_root != null) {
- urlRoot = gon.relative_url_root;
- }
- return urlRoot + url.replace(':version', gon.api_version);
+ return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
},
};
diff --git a/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
index 55c68139ded..b7200150925 100644
--- a/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
+++ b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import { parseBoolean } from '~/lib/utils/common_utils';
-import GfmAutoComplete from '~/gfm_auto_complete';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
export default function initGFMInput() {
$('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index 6aa689b4056..17de7b2cf1e 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -168,7 +168,7 @@ export default {
</script>
<template>
<div>
- <div class="d-flex board-card-header">
+ <div class="d-flex board-card-header" dir="auto">
<h4 class="board-card-title append-bottom-0 prepend-top-0">
<icon
v-if="issue.confidential"
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 6cf77705847..7e5d0e0f888 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -90,6 +90,7 @@ class List {
this.id = data.id;
this.type = data.list_type;
this.position = data.position;
+ this.label = data.label;
return this.getIssues();
});
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index df855261b3c..7f07e592f84 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -1,5 +1,6 @@
import Visibility from 'visibilityjs';
import Vue from 'vue';
+import { GlToast } from '@gitlab/ui';
import PersistentUserCallout from '../persistent_user_callout';
import { s__, sprintf } from '../locale';
import Flash from '../flash';
@@ -20,6 +21,8 @@ import ClustersStore from './stores/clusters_store';
import Applications from './components/applications.vue';
import setupToggleButtons from '../toggle_buttons';
+Vue.use(GlToast);
+
/**
* Cluster page has 2 separate parts:
* Toggle button and applications section
@@ -135,7 +138,6 @@ export default class Clusters {
eventHub.$on('installApplication', this.installApplication);
eventHub.$on('upgradeApplication', data => this.upgradeApplication(data));
eventHub.$on('upgradeFailed', appId => this.upgradeFailed(appId));
- eventHub.$on('dismissUpgradeSuccess', appId => this.dismissUpgradeSuccess(appId));
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
}
@@ -145,7 +147,6 @@ export default class Clusters {
eventHub.$off('installApplication', this.installApplication);
eventHub.$off('upgradeApplication', this.upgradeApplication);
eventHub.$off('upgradeFailed', this.upgradeFailed);
- eventHub.$off('dismissUpgradeSuccess', this.dismissUpgradeSuccess);
eventHub.$off('saveKnativeDomain');
eventHub.$off('setKnativeHostname');
}
@@ -283,10 +284,6 @@ export default class Clusters {
this.store.updateAppProperty(appId, 'requestStatus', UPGRADE_REQUEST_FAILURE);
}
- dismissUpgradeSuccess(appId) {
- this.store.updateAppProperty(appId, 'requestStatus', null);
- }
-
toggleIngressDomainHelpText(ingressPreviousState, ingressNewState) {
const { externalIp, status } = ingressNewState;
const helpTextHidden = status !== APPLICATION_STATUS.INSTALLED || !externalIp;
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index 937e4c3bfc3..17fe816e62b 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -241,6 +241,8 @@ export default {
status() {
if (this.status === APPLICATION_STATUS.UPDATE_ERRORED) {
eventHub.$emit('upgradeFailed', this.id);
+ } else if (this.upgradeRequested && this.upgradeSuccessful) {
+ this.$toast.show(this.upgradeSuccessDescription);
}
},
},
@@ -257,9 +259,6 @@ export default {
params: this.installApplicationRequestParams,
});
},
- dismissUpgradeSuccess() {
- eventHub.$emit('dismissUpgradeSuccess', this.id);
- },
},
};
</script>
@@ -335,17 +334,6 @@ export default {
>
{{ upgradeFailureDescription }}
</div>
-
- <div
- v-if="upgradeRequested && upgradeSuccessful"
- class="bs-callout bs-callout-success cluster-application-banner mt-2 mb-0 p-0 pl-3"
- >
- {{ upgradeSuccessDescription }}
- <button class="close cluster-application-banner-close" @click="dismissUpgradeSuccess">
- &times;
- </button>
- </div>
-
<loading-button
v-if="upgradeAvailable || upgradeFailed || isUpgrading"
class="btn btn-primary js-cluster-application-upgrade-button mt-2"
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index f1e26cdfa21..f437954881c 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -477,6 +477,16 @@ class GfmAutoComplete {
}
return null;
},
+ highlighter(li, query) {
+ // override default behaviour to escape dot character
+ // see https://github.com/ichord/At.js/pull/576
+ if (!query) {
+ return li;
+ }
+ const escapedQuery = query.replace(/[.+]/, '\\$&');
+ const regexp = new RegExp(`>\\s*([^<]*?)(${escapedQuery})([^<]*)\\s*<`, 'ig');
+ return li.replace(regexp, (str, $1, $2, $3) => `> ${$1}<strong>${$2}</strong>${$3} <`);
+ },
};
}
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index f5e2e46237f..5a6d44ef838 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import autosize from 'autosize';
-import GfmAutoComplete, * as GFMConfig from './gfm_auto_complete';
+import GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_complete';
import dropzoneInput from './dropzone_input';
import { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown';
@@ -8,7 +8,7 @@ export default class GLForm {
constructor(form, enableGFM = {}) {
this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input');
- this.enableGFM = Object.assign({}, GFMConfig.defaultAutocompleteConfig, enableGFM);
+ this.enableGFM = Object.assign({}, defaultAutocompleteConfig, enableGFM);
// Disable autocomplete for keywords which do not have dataSources available
const dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {};
Object.keys(this.enableGFM).forEach(item => {
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
index 00b2d236da3..6b0aa5b2b2b 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
@@ -108,6 +108,7 @@ export default {
:placeholder="placeholder"
:value="text"
class="note-textarea ide-commit-message-textarea"
+ dir="auto"
name="commit-message"
@scroll="handleScroll"
@input="onInput"
diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
index ec759043efc..188518dd419 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
@@ -57,6 +57,8 @@ export default {
type: 'blob',
content: result,
base64: !isText,
+ binary: !isText,
+ rawPath: !isText ? target.result : '',
});
},
readFile(file) {
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 94a9e87369c..e15b2a6f76b 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -1,5 +1,6 @@
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
+import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
import flash from '~/flash';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
@@ -35,7 +36,7 @@ export default {
]),
...mapGetters('fileTemplates', ['showFileTemplatesBar']),
shouldHideEditor() {
- return this.file && this.file.binary && !this.file.content;
+ return this.file && this.file.binary;
},
showContentViewer() {
return (
@@ -56,6 +57,10 @@ export default {
active: this.file.viewMode === 'preview',
};
},
+ fileType() {
+ const info = viewerInformationForPath(this.file.path);
+ return (info && info.id) || '';
+ },
},
watch: {
file(newVal, oldVal) {
@@ -258,6 +263,7 @@ export default {
:path="file.rawPath || file.path"
:file-size="file.size"
:project-path="file.projectId"
+ :type="fileType"
/>
<diff-viewer
v-if="showDiffViewer"
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index 7c560c89695..e30670e119f 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -72,4 +72,11 @@ export const modalTypes = {
tree: 'tree',
};
+export const commitActionTypes = {
+ move: 'move',
+ delete: 'delete',
+ create: 'create',
+ update: 'update',
+};
+
export const packageJsonPath = 'package.json';
diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js
index 229ef168926..518a9cf7a0f 100644
--- a/app/assets/javascripts/ide/ide_router.js
+++ b/app/assets/javascripts/ide/ide_router.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
-import { join as joinPath } from 'path';
+import { joinPaths } from '~/lib/utils/url_utility';
import flash from '~/flash';
import store from './stores';
@@ -34,7 +34,7 @@ const EmptyRouterComponent = {
const router = new VueRouter({
mode: 'history',
- base: `${gon.relative_url_root}/-/ide/`,
+ base: joinPaths(gon.relative_url_root || '', '/-/ide/'),
routes: [
{
path: '/project/:namespace+/:project',
@@ -46,11 +46,11 @@ const router = new VueRouter({
},
{
path: ':targetmode(edit|tree|blob)/:branchid+/',
- redirect: to => joinPath(to.path, '/-/'),
+ redirect: to => joinPaths(to.path, '/-/'),
},
{
path: ':targetmode(edit|tree|blob)',
- redirect: to => joinPath(to.path, '/master/-/'),
+ redirect: to => joinPaths(to.path, '/master/-/'),
},
{
path: 'merge_requests/:mrid',
@@ -58,7 +58,7 @@ const router = new VueRouter({
},
{
path: '',
- redirect: to => joinPath(to.path, '/edit/master/-/'),
+ redirect: to => joinPaths(to.path, '/edit/master/-/'),
},
],
},
diff --git a/app/assets/javascripts/ide/lib/files.js b/app/assets/javascripts/ide/lib/files.js
index df100f753d7..b8abaa41f23 100644
--- a/app/assets/javascripts/ide/lib/files.js
+++ b/app/assets/javascripts/ide/lib/files.js
@@ -22,6 +22,8 @@ export const decorateFiles = ({
tempFile = false,
content = '',
base64 = false,
+ binary = false,
+ rawPath = '',
}) => {
const treeList = [];
const entries = {};
@@ -90,6 +92,8 @@ export const decorateFiles = ({
changed: tempFile,
content,
base64,
+ binary,
+ rawPath,
previewMode: viewerInformationForPath(name),
parentPath,
});
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 7b660bda081..fd678e6e10c 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -53,7 +53,7 @@ export const setResizingStatus = ({ commit }, resizing) => {
export const createTempEntry = (
{ state, commit, dispatch },
- { name, type, content = '', base64 = false },
+ { name, type, content = '', base64 = false, binary = false, rawPath = '' },
) =>
new Promise(resolve => {
const fullName = name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name;
@@ -79,8 +79,10 @@ export const createTempEntry = (
branchId: state.currentBranchId,
type,
tempFile: true,
- base64,
content,
+ base64,
+ binary,
+ rawPath,
});
const { file, parentPath } = data;
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
index b7090e09daf..59ead8a3dcf 100644
--- a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js
@@ -23,22 +23,27 @@ export const receiveTemplateTypesError = ({ commit, dispatch }) => {
export const receiveTemplateTypesSuccess = ({ commit }, templates) =>
commit(types.RECEIVE_TEMPLATE_TYPES_SUCCESS, templates);
-export const fetchTemplateTypes = ({ dispatch, state, rootState }, page = 1) => {
+export const fetchTemplateTypes = ({ dispatch, state, rootState }) => {
if (!Object.keys(state.selectedTemplateType).length) return Promise.reject();
dispatch('requestTemplateTypes');
- return Api.projectTemplates(rootState.currentProjectId, state.selectedTemplateType.key, { page })
- .then(({ data, headers }) => {
- const nextPage = parseInt(normalizeHeaders(headers)['X-NEXT-PAGE'], 10);
+ const fetchPages = (page = 1, prev = []) =>
+ Api.projectTemplates(rootState.currentProjectId, state.selectedTemplateType.key, {
+ page,
+ per_page: 100,
+ })
+ .then(({ data, headers }) => {
+ const nextPage = parseInt(normalizeHeaders(headers)['X-NEXT-PAGE'], 10);
+ const nextData = prev.concat(data);
- dispatch('receiveTemplateTypesSuccess', data);
+ dispatch('receiveTemplateTypesSuccess', nextData);
- if (nextPage) {
- dispatch('fetchTemplateTypes', nextPage);
- }
- })
- .catch(() => dispatch('receiveTemplateTypesError'));
+ return nextPage ? fetchPages(nextPage, nextData) : nextData;
+ })
+ .catch(() => dispatch('receiveTemplateTypesError'));
+
+ return fetchPages();
};
export const setSelectedTemplateType = ({ commit, dispatch, rootGetters }, type) => {
diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js b/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js
index 25a65b047f1..7fc1c9134a7 100644
--- a/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js
+++ b/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js
@@ -3,13 +3,14 @@ import * as types from './mutation_types';
export default {
[types.REQUEST_TEMPLATE_TYPES](state) {
state.isLoading = true;
+ state.templates = [];
},
[types.RECEIVE_TEMPLATE_TYPES_ERROR](state) {
state.isLoading = false;
},
[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, templates) {
state.isLoading = false;
- state.templates = state.templates.concat(templates);
+ state.templates = templates;
},
[types.SET_SELECTED_TEMPLATE_TYPE](state, type) {
state.selectedTemplateType = type;
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index 9b9f4b21f1c..344b189decf 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -213,7 +213,7 @@ export default {
? `${slashedParentPath}${oldEntry.name}`
: `${slashedParentPath}${name}`;
- state.entries[newPath] = {
+ Vue.set(state.entries, newPath, {
...oldEntry,
id: newPath,
key: `${newPath}-${oldEntry.type}-${oldEntry.id}`,
@@ -225,7 +225,7 @@ export default {
tree: [],
parentPath,
raw: '',
- };
+ });
oldEntry.moved = true;
oldEntry.movedPath = newPath;
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index 3ab8f3f11be..bcc9ca60d9b 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -1,3 +1,5 @@
+import { commitActionTypes } from '../constants';
+
export const dataStructure = () => ({
id: '',
// Key will contain a mixture of ID and path
@@ -69,6 +71,8 @@ export const decorateData = entity => {
changed = false,
parentTreeUrl = '',
base64 = false,
+ binary = false,
+ rawPath = '',
previewMode,
file_lock,
html,
@@ -92,6 +96,8 @@ export const decorateData = entity => {
renderError,
content,
base64,
+ binary,
+ rawPath,
previewMode,
file_lock,
html,
@@ -110,14 +116,14 @@ export const setPageTitle = title => {
export const commitActionForFile = file => {
if (file.prevPath) {
- return 'move';
+ return commitActionTypes.move;
} else if (file.deleted) {
- return 'delete';
+ return commitActionTypes.delete;
} else if (file.tempFile) {
- return 'create';
+ return commitActionTypes.create;
}
- return 'update';
+ return commitActionTypes.update;
};
export const getCommitFiles = stagedFiles =>
diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js
index 9336b71cfd7..7576d36f27d 100644
--- a/app/assets/javascripts/issuable_form.js
+++ b/app/assets/javascripts/issuable_form.js
@@ -1,8 +1,8 @@
import $ from 'jquery';
import Pikaday from 'pikaday';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import Autosave from './autosave';
import UsersSelect from './users_select';
-import GfmAutoComplete from './gfm_auto_complete';
import ZenMode from './zen_mode';
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index 385e9543973..f2462e50093 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -149,6 +149,7 @@ export default {
v-model="descriptionText"
:data-update-url="updateUrl"
class="hidden js-task-list-field"
+ dir="auto"
>
</textarea>
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index 299130e56ae..d27dd873125 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -53,6 +53,7 @@ export default {
v-model="formState.description"
class="note-textarea js-gfm-input js-autosize markdown-area
qa-description-textarea"
+ dir="auto"
data-supports-quick-actions="false"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
diff --git a/app/assets/javascripts/issue_show/components/fields/title.vue b/app/assets/javascripts/issue_show/components/fields/title.vue
index a3371cb9614..ce4baf17d09 100644
--- a/app/assets/javascripts/issue_show/components/fields/title.vue
+++ b/app/assets/javascripts/issue_show/components/fields/title.vue
@@ -20,6 +20,7 @@ export default {
ref="input"
v-model="formState.title"
class="form-control qa-title-input"
+ dir="auto"
type="text"
placeholder="Title"
aria-label="Title"
diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue
index 3b5c95ccded..d2f33dc31a7 100644
--- a/app/assets/javascripts/issue_show/components/title.vue
+++ b/app/assets/javascripts/issue_show/components/title.vue
@@ -72,6 +72,7 @@ export default {
'issue-realtime-trigger-pulse': pulseAnimation,
}"
class="title"
+ dir="auto"
v-html="titleHtml"
></h2>
<button
diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js
index ae02559415c..498c2348ca2 100644
--- a/app/assets/javascripts/lib/graphql.js
+++ b/app/assets/javascripts/lib/graphql.js
@@ -3,10 +3,17 @@ import { InMemoryCache } from 'apollo-cache-inmemory';
import { createUploadLink } from 'apollo-upload-client';
import csrf from '~/lib/utils/csrf';
-export default (resolvers = {}) =>
- new ApolloClient({
+export default (resolvers = {}, baseUrl = '') => {
+ let uri = `${gon.relative_url_root}/api/graphql`;
+
+ if (baseUrl) {
+ // Prepend baseUrl and ensure that `///` are replaced with `/`
+ uri = `${baseUrl}${uri}`.replace(/\/{3,}/g, '/');
+ }
+
+ return new ApolloClient({
link: createUploadLink({
- uri: `${gon.relative_url_root}/api/graphql`,
+ uri,
headers: {
[csrf.headerKey]: csrf.token,
},
@@ -14,3 +21,4 @@ export default (resolvers = {}) =>
cache: new InMemoryCache(),
resolvers,
});
+};
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 4ba84589705..bdfd06fc250 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -120,3 +120,5 @@ export function webIDEUrl(route = undefined) {
}
return returnUrl;
}
+
+export { join as joinPaths } from 'path';
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index f2bd4150b6d..00547abd7bc 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -221,6 +221,7 @@ export default {
<gl-dropdown-item
v-for="environment in store.environmentsData"
:key="environment.id"
+ :href="environment.metrics_path"
:active="environment.name === currentEnvironmentName"
active-class="is-active"
>{{ environment.name }}</gl-dropdown-item
diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js
index ed794779ff2..08dc57d545c 100644
--- a/app/assets/javascripts/monitoring/monitoring_bundle.js
+++ b/app/assets/javascripts/monitoring/monitoring_bundle.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
-import Dashboard from './components/dashboard.vue';
+import Dashboard from 'ee_else_ce/monitoring/components/dashboard.vue';
export default (props = {}) => {
const el = document.getElementById('prometheus-graphs');
diff --git a/app/assets/javascripts/mr_popover/index.js b/app/assets/javascripts/mr_popover/index.js
index 9a97e98f9db..18c0e201300 100644
--- a/app/assets/javascripts/mr_popover/index.js
+++ b/app/assets/javascripts/mr_popover/index.js
@@ -22,13 +22,10 @@ const handleUserPopoverMouseOut = ({ target }) => {
* Adds a MergeRequestPopover component to the body, hands over as much data as the target element has in data attributes.
* loads based on data-project-path and data-iid more data about an MR from the API and sets it on the popover
*/
-const handleMRPopoverMount = apolloProvider => ({ target }) => {
+const handleMRPopoverMount = ({ apolloProvider, projectPath, mrTitle, iid }) => ({ target }) => {
// Add listener to actually remove it again
target.addEventListener('mouseleave', handleUserPopoverMouseOut);
- const { projectPath, mrTitle, iid } = target.dataset;
- const mergeRequest = {};
-
renderFn = setTimeout(() => {
const MRPopoverComponent = Vue.extend(MRPopover);
renderedPopover = new MRPopoverComponent({
@@ -36,7 +33,6 @@ const handleMRPopoverMount = apolloProvider => ({ target }) => {
target,
projectPath,
mergeRequestIID: iid,
- mergeRequest,
mergeRequestTitle: mrTitle,
},
apolloProvider,
@@ -57,8 +53,13 @@ export default elements => {
const listenerAddedAttr = 'data-mr-listener-added';
mrLinks.forEach(el => {
- if (!el.getAttribute(listenerAddedAttr)) {
- el.addEventListener('mouseenter', handleMRPopoverMount(apolloProvider));
+ const { projectPath, mrTitle, iid } = el.dataset;
+
+ if (!el.getAttribute(listenerAddedAttr) && projectPath && mrTitle && iid) {
+ el.addEventListener(
+ 'mouseenter',
+ handleMRPopoverMount({ apolloProvider, projectPath, mrTitle, iid }),
+ );
el.setAttribute(listenerAddedAttr, true);
}
});
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index caf22f71bf9..688c06878ac 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -351,6 +351,7 @@ Please check your network connection and try again.`;
ref="textarea"
slot="textarea"
v-model="note"
+ dir="auto"
:disabled="isSubmitting"
name="note[note]"
class="note-textarea js-vue-comment-form js-note-text
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index fbf75ed0e41..8ddd5b8514a 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -122,6 +122,7 @@ export default {
v-model="note.note"
:data-update-url="note.path"
class="hidden js-task-list-field"
+ dir="auto"
></textarea>
<note-edited-text
v-if="note.last_edited_at"
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index 471323bfc83..fb098095cf3 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -268,6 +268,7 @@ export default {
:data-supports-quick-actions="!isEditing"
name="note[note]"
class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input"
+ dir="auto"
aria-label="Description"
placeholder="Write a comment or drag your files here…"
@keydown.meta.enter="handleKeySubmit()"
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 89563711bcd..1e47bef7b61 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -454,8 +454,13 @@ Please check your network connection and try again.`;
</component>
</template>
</ul>
+ <draft-note
+ v-if="showDraft(discussion.reply_id)"
+ :key="`draft_${discussion.id}`"
+ :draft="draftForDiscussion(discussion.reply_id)"
+ />
<div
- v-if="isExpanded || !hasReplies"
+ v-else-if="isExpanded || !hasReplies"
:class="{ 'is-replying': isReplying }"
class="discussion-reply-holder"
>
diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js
index 0dd0d5336fc..e726ab0e220 100644
--- a/app/assets/javascripts/pages/profiles/show/index.js
+++ b/app/assets/javascripts/pages/profiles/show/index.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import createFlash from '~/flash';
-import GfmAutoComplete from '~/gfm_auto_complete';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import emojiRegex from 'emoji-regex';
import EmojiMenu from './emoji_menu';
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
index 7f86741ed29..c0659a0173a 100644
--- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
@@ -2,7 +2,7 @@
import $ from 'jquery';
import createFlash from '~/flash';
import Icon from '~/vue_shared/components/icon.vue';
-import GfmAutoComplete from '~/gfm_auto_complete';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import { __, s__ } from '~/locale';
import Api from '~/api';
import { GlModal, GlTooltipDirective } from '@gitlab/ui';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue
index a38f25cce35..acd8037cfb2 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue
@@ -30,6 +30,7 @@ export default {
:id="inputId"
:value="value"
class="form-control js-gfm-input append-bottom-default commit-message-edit"
+ dir="auto"
required="required"
rows="7"
@input="$emit('input', $event.target.value)"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index bb76eb1030d..851939d5d4e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -3,6 +3,7 @@ import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll';
import { __ } from '~/locale';
+import readyToMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/ready_to_merge';
import MergeRequest from '../../../merge_request';
import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue';
@@ -21,6 +22,7 @@ export default {
CommitEdit,
CommitMessageDropdown,
},
+ mixins: [readyToMergeMixin],
props: {
mr: { type: Object, required: true },
service: { type: Object, required: true },
@@ -94,15 +96,6 @@ export default {
shouldShowMergeOptionsDropdown() {
return this.mr.isPipelineActive && !this.mr.onlyAllowMergeIfPipelineSucceeds;
},
- isMergeButtonDisabled() {
- const { commitMessage } = this;
- return Boolean(
- !commitMessage.length ||
- !this.shouldShowMergeControls ||
- this.isMakingRequest ||
- this.mr.preventMerge,
- );
- },
isRemoveSourceBranchButtonDisabled() {
return this.isMergeButtonDisabled;
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
new file mode 100644
index 00000000000..b2e64506472
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
@@ -0,0 +1,13 @@
+export default {
+ computed: {
+ isMergeButtonDisabled() {
+ const { commitMessage } = this;
+ return Boolean(
+ !commitMessage.length ||
+ !this.shouldShowMergeControls ||
+ this.isMakingRequest ||
+ this.mr.preventMerge,
+ );
+ },
+ },
+};
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue
index 4155e1bab9c..1e6f4c376c1 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue
@@ -1,5 +1,4 @@
<script>
-import { viewerInformationForPath } from './lib/viewer_utils';
import MarkdownViewer from './viewers/markdown_viewer.vue';
import ImageViewer from './viewers/image_viewer.vue';
import DownloadViewer from './viewers/download_viewer.vue';
@@ -24,15 +23,18 @@ export default {
required: false,
default: '',
},
+ type: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
viewer() {
if (!this.path) return null;
+ if (!this.type) return DownloadViewer;
- const previewInfo = viewerInformationForPath(this.path);
- if (!previewInfo) return DownloadViewer;
-
- switch (previewInfo.id) {
+ switch (this.type) {
case 'markdown':
return MarkdownViewer;
case 'image':
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
index ad3b3b81ac5..8d77b156aa4 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
@@ -58,12 +58,11 @@ export default {
const moveX = e.pageX || e.touches[0].pageX;
let leftValue = moveX - this.$refs.swipeFrame.getBoundingClientRect().left;
- const spaceLeft = 20;
const { clientWidth } = this.$refs.swipeFrame;
if (leftValue <= 0) {
leftValue = 0;
- } else if (leftValue > clientWidth - spaceLeft) {
- leftValue = clientWidth - spaceLeft;
+ } else if (leftValue > clientWidth) {
+ leftValue = clientWidth;
}
this.swipeWrapWidth = (leftValue / clientWidth) * 100;
@@ -80,7 +79,7 @@ export default {
document.body.removeEventListener('touchmove', this.dragMove);
},
prepareSwipe() {
- if (this.swipeOldImgInfo && this.swipeNewImgInfo) {
+ if (this.swipeOldImgInfo && this.swipeNewImgInfo && this.swipeOldImgInfo.renderedWidth > 0) {
// Add 2 for border width
this.swipeMaxWidth =
Math.max(this.swipeOldImgInfo.renderedWidth, this.swipeNewImgInfo.renderedWidth) + 2;
@@ -101,6 +100,8 @@ export default {
},
resize: _.throttle(function throttledResize() {
this.swipeBarPos = 0;
+ this.swipeWrapWidth = 0;
+ this.prepareSwipe();
}, 400),
},
};
@@ -111,6 +112,8 @@ export default {
<div
ref="swipeFrame"
:style="{
+ width: swipeMaxPixelWidth,
+ height: swipeMaxPixelHeight,
'user-select': dragging ? 'none' : null,
}"
class="swipe-frame"
diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
index bf96ce0bafb..ffde55bf083 100644
--- a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
+++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
@@ -45,10 +45,10 @@ export default {
'issuable-info-container': !canReorder,
'card-body': canReorder,
}"
- class="item-body"
+ class="item-body d-flex align-items-center p-2 p-lg-3 p-xl-2 pl-xl-3"
>
- <div class="item-contents">
- <div class="item-title d-flex align-items-center">
+ <div class="item-contents d-flex align-items-center flex-wrap flex-grow-1 flex-xl-nowrap">
+ <div class="item-title d-flex align-items-center mb-1 mb-xl-0">
<icon
v-if="hasState"
v-tooltip
@@ -65,13 +65,15 @@ export default {
name="eye-slash"
:size="16"
:title="__('Confidential')"
- class="confidential-icon append-right-4"
+ class="confidential-icon append-right-4 align-self-baseline align-self-md-auto mt-xl-0"
:aria-label="__('Confidential')"
/>
<a :href="computedPath" class="sortable-link">{{ title }}</a>
</div>
- <div class="item-meta">
- <div class="d-flex align-items-center item-path-id">
+ <div class="item-meta d-flex flex-wrap mt-xl-0 justify-content-xl-end flex-xl-nowrap">
+ <div
+ class="d-flex align-items-center item-path-id order-md-0 mt-md-0 mt-1 ml-xl-2 mr-xl-auto"
+ >
<icon
v-if="hasState"
v-tooltip
@@ -88,7 +90,9 @@ export default {
}}</span>
{{ pathIdSeparator }}{{ itemId }}
</div>
- <div class="item-meta-child d-flex align-items-center">
+ <div
+ class="item-meta-child d-flex align-items-center order-0 flex-wrap mr-md-1 ml-md-auto ml-xl-2 flex-xl-nowrap"
+ >
<span v-if="hasPipeline" class="mr-ci-status pr-2">
<a :href="pipelineStatus.details_path">
<ci-icon v-gl-tooltip :status="pipelineStatus" :title="pipelineStatusTooltip" />
@@ -105,7 +109,7 @@ export default {
<issue-assignees
v-if="assignees.length"
:assignees="assignees"
- class="item-assignees d-inline-flex"
+ class="item-assignees d-inline-flex align-items-center align-self-end ml-auto ml-md-0 mb-md-0 order-2 flex-xl-grow-0 mt-xl-0 mr-xl-1"
/>
</div>
</div>
@@ -115,7 +119,7 @@ export default {
v-tooltip
:disabled="removeDisabled"
type="button"
- class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button"
+ class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button mr-xl-0 align-self-xl-center"
title="Remove"
aria-label="Remove"
@click="onRemoveRequest"
diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
index f9773622001..4dbfd1ba6f4 100644
--- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
+++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
@@ -1,11 +1,13 @@
<script>
import { GlPopover, GlSkeletonLoading } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
import UserAvatarImage from '../user_avatar/user_avatar_image.vue';
import { glEmojiTag } from '../../../emoji';
export default {
name: 'UserPopover',
components: {
+ Icon,
GlPopover,
GlSkeletonLoading,
UserAvatarImage,
@@ -68,16 +70,27 @@ export default {
<gl-skeleton-loading v-else :lines="1" class="animation-container-small mb-1" />
</div>
<div class="text-secondary">
- <div v-if="user.bio" class="js-bio">{{ user.bio }}</div>
- <div v-if="user.organization" class="js-organization">{{ user.organization }}</div>
+ <div v-if="user.bio" class="js-bio d-flex mb-1">
+ <icon name="profile" css-classes="category-icon" />
+ <span class="ml-1">{{ user.bio }}</span>
+ </div>
+ <div v-if="user.organization" class="js-organization d-flex mb-1">
+ <icon v-show="!jobInfoIsLoading" name="work" css-classes="category-icon" />
+ <span class="ml-1">{{ user.organization }}</span>
+ </div>
<gl-skeleton-loading
v-if="jobInfoIsLoading"
:lines="1"
class="animation-container-small mb-1"
/>
</div>
- <div class="text-secondary">
- {{ user.location }}
+ <div class="js-location text-secondary d-flex">
+ <icon
+ v-show="!locationIsLoading && user.location"
+ name="location"
+ css-classes="category-icon"
+ />
+ <span class="ml-1">{{ user.location }}</span>
<gl-skeleton-loading
v-if="locationIsLoading"
:lines="1"
diff --git a/app/assets/stylesheets/components/popover.scss b/app/assets/stylesheets/components/popover.scss
index 7d46b262a69..838bf5d343b 100644
--- a/app/assets/stylesheets/components/popover.scss
+++ b/app/assets/stylesheets/components/popover.scss
@@ -5,6 +5,10 @@
padding: $gl-padding-8;
font-size: $gl-font-size-small;
line-height: $gl-line-height;
+
+ .category-icon {
+ color: $gray-600;
+ }
}
}
diff --git a/app/assets/stylesheets/components/related_items_list.scss b/app/assets/stylesheets/components/related_items_list.scss
index 0fdb11d311a..7f9cf1266b1 100644
--- a/app/assets/stylesheets/components/related_items_list.scss
+++ b/app/assets/stylesheets/components/related_items_list.scss
@@ -16,10 +16,7 @@ $item-weight-max-width: 48px;
}
.item-body {
- display: flex;
position: relative;
- align-items: center;
- padding: $gl-padding-8;
line-height: $gl-line-height;
.issue-token-state-icon-open {
@@ -49,14 +46,11 @@ $item-weight-max-width: 48px;
}
.confidential-icon {
- align-self: baseline;
color: $orange-600;
- margin-right: $gl-padding-4;
}
.item-title {
flex-basis: 100%;
- margin-bottom: $gl-padding-8;
font-size: $gl-font-size-small;
&.mr-title {
@@ -80,24 +74,12 @@ $item-weight-max-width: 48px;
}
}
-.item-contents {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- flex-grow: 1;
-}
-
.item-meta {
- display: flex;
- flex-wrap: wrap;
flex-basis: 100%;
font-size: $gl-font-size-small;
color: $gl-text-color-secondary;
.item-meta-child {
- order: 0;
- display: flex;
- flex-wrap: wrap;
flex-basis: 100%;
}
@@ -117,16 +99,10 @@ $item-weight-max-width: 48px;
}
.item-weight {
- margin-right: 0;
max-width: $item-weight-max-width;
}
.item-assignees {
- order: 2;
- align-self: flex-end;
- align-items: center;
- margin-left: auto;
-
.user-avatar-link {
margin-right: -$gl-padding-4;
@@ -162,7 +138,6 @@ $item-weight-max-width: 48px;
}
.item-path-id {
- margin-top: $gl-padding-4;
font-size: $gl-font-size-xs;
white-space: nowrap;
@@ -176,8 +151,10 @@ $item-weight-max-width: 48px;
display: block;
}
- &:not(.mr-item-path) {
- order: 1;
+ @include media-breakpoint-down(sm) {
+ &:not(.mr-item-path) {
+ order: 1;
+ }
}
}
@@ -219,15 +196,14 @@ $item-weight-max-width: 48px;
.item-body {
.item-contents {
min-width: 0;
+ }
- .item-title {
- flex-basis: unset;
- // 95% because we compensate
- // for remove button which is
- // positioned absolutely
- width: 95%;
- margin-bottom: $gl-padding-4;
- }
+ .item-title {
+ flex-basis: unset;
+ // 95% because we compensate
+ // for remove button which is
+ // positioned absolutely
+ width: 95%;
}
.btn-item-remove {
@@ -236,34 +212,19 @@ $item-weight-max-width: 48px;
}
.item-meta {
- .item-path-id {
- order: 0;
- margin-top: 0;
- }
-
.item-meta-child {
flex-basis: unset;
- margin-left: auto;
- margin-right: $gl-padding-4;
~ .item-assignees {
margin-left: $gl-padding-4;
}
}
-
- .item-assignees {
- margin-bottom: 0;
- margin-left: 0;
- order: 2;
- }
}
}
/* Medium devices (desktops, 992px and up) */
@include media-breakpoint-up(lg) {
.item-body {
- padding: $gl-padding;
-
.item-title {
font-size: $gl-font-size;
}
@@ -277,12 +238,7 @@ $item-weight-max-width: 48px;
/* Large devices (large desktops, 1200px and up) */
@include media-breakpoint-up(xl) {
.item-body {
- padding: $gl-padding-8;
- padding-left: $gl-padding;
-
.item-title {
- display: flex;
- margin-bottom: 0;
min-width: 0;
width: auto;
flex-basis: unset;
@@ -293,43 +249,18 @@ $item-weight-max-width: 48px;
display: block;
margin-right: $gl-padding-8;
}
-
- .confidential-icon {
- align-self: auto;
- margin-top: 0;
- }
}
}
.item-contents {
- flex-wrap: nowrap;
overflow: hidden;
}
.item-meta {
- margin-top: 0;
- justify-content: flex-end;
flex: 1;
- flex-wrap: nowrap;
-
- .item-meta-child {
- margin-left: $gl-padding-8;
- flex-wrap: nowrap;
- }
- }
-
- .item-path-id {
- order: 0;
- margin-top: 0;
- margin-left: $gl-padding-8;
- margin-right: auto;
}
.item-assignees {
- flex-grow: 0;
- margin-top: 0;
- margin-right: $gl-padding-4;
-
.avatar {
height: $gl-padding-24;
width: $gl-padding-24;
@@ -345,10 +276,8 @@ $item-weight-max-width: 48px;
.btn-item-remove {
position: relative;
- align-self: center;
top: initial;
right: 0;
- margin-right: 0;
padding: $btn-sm-side-margin;
&:hover {
@@ -357,10 +286,6 @@ $item-weight-max-width: 48px;
}
.sortable-link {
- display: block;
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
line-height: 1.3;
}
}
diff --git a/app/assets/stylesheets/components/toast.scss b/app/assets/stylesheets/components/toast.scss
new file mode 100644
index 00000000000..91d16c8e98d
--- /dev/null
+++ b/app/assets/stylesheets/components/toast.scss
@@ -0,0 +1,3 @@
+.toast-close {
+ font-size: $default-icon-size !important;
+}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 61fab445793..02a95c04b8b 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -256,6 +256,10 @@
}
}
+.board-card-header {
+ text-align: initial;
+}
+
.board-card-assignee {
margin-top: -$gl-padding-4;
margin-bottom: -$gl-padding-4;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index f8620eec46d..04c66006027 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -60,6 +60,7 @@
overflow-wrap: break-word;
min-width: 0;
width: 100%;
+ text-align: initial;
}
.btn-edit {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 60a840aac1b..13288d8bad1 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -456,7 +456,9 @@
// Don't hide the overflow in system messages
.system-note-message,
-.issuable-detail {
+.issuable-detail,
+.md-preview-holder,
+.note-body {
.scoped-label-wrapper {
.badge {
overflow: initial;
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index d5c4712bd78..4926062f9ca 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -8,13 +8,6 @@
module AuthenticatesWithTwoFactor
extend ActiveSupport::Concern
- included do
- # This action comes from DeviseController, but because we call `sign_in`
- # manually, not skipping this action would cause a "You are already signed
- # in." error message to be shown upon successful login.
- skip_before_action :require_no_authentication, only: [:create], raise: false
- end
-
# Store the user's ID in the session for later retrieval and render the
# two factor code prompt
#
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index c0c0160a827..cfff154c3dd 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -31,7 +31,7 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_labels_tab", {
- labels: @milestone.labels # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ labels: @milestone.labels.map { |label| label.present(issuable_subject: @milestone.parent) } # rubocop:disable Gitlab/ModuleWithInstanceVariables
})
end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index e14abbf7c78..fc708400657 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -100,14 +100,14 @@ class Projects::BranchesController < Projects::ApplicationController
respond_to do |format|
format.html do
- flash_type = result[:status] == :error ? :alert : :notice
- flash[flash_type] = result[:message]
+ flash_type = result.error? ? :alert : :notice
+ flash[flash_type] = result.message
redirect_to project_branches_path(@project), status: :see_other
end
- format.js { head result[:return_code] }
- format.json { render json: { message: result[:message] }, status: result[:return_code] }
+ format.js { head result.http_status }
+ format.json { render json: { message: result.message }, status: result.http_status }
end
end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index d1c5cef76fa..c4dff95a4b9 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -19,7 +19,7 @@ module Projects
redirect_to project_settings_ci_cd_path(@project)
else
- render 'show'
+ redirect_to project_settings_ci_cd_path(@project), alert: result[:message]
end
end
end
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 88910c91763..fa5bdbc7d49 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -17,7 +17,7 @@ class Projects::WikisController < Projects::ApplicationController
def pages
@wiki_pages = Kaminari.paginate_array(
- @project_wiki.pages(sort: params[:sort], direction: params[:direction])
+ @project_wiki.list_pages(sort: params[:sort], direction: params[:direction])
).page(params[:page])
@wiki_entries = WikiPage.group_by_directory(@wiki_pages)
@@ -118,7 +118,7 @@ class Projects::WikisController < Projects::ApplicationController
@sidebar_page = @project_wiki.find_sidebar(params[:version_id])
unless @sidebar_page # Fallback to default sidebar
- @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages(limit: 15))
+ @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.list_pages(limit: 15))
end
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = _("Could not create Wiki Repository at this time. Please try again later.")
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 6943795e8ac..6fea61cf45d 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -8,6 +8,8 @@ class SessionsController < Devise::SessionsController
include Recaptcha::Verify
skip_before_action :check_two_factor_requirement, only: [:destroy]
+ # replaced with :require_no_authentication_without_flash
+ skip_before_action :require_no_authentication, only: [:new, :create]
prepend_before_action :check_initial_setup, only: [:new]
prepend_before_action :authenticate_with_two_factor,
@@ -15,6 +17,8 @@ class SessionsController < Devise::SessionsController
prepend_before_action :check_captcha, only: [:create]
prepend_before_action :store_redirect_uri, only: [:new]
prepend_before_action :ldap_servers, only: [:new, :create]
+ prepend_before_action :require_no_authentication_without_flash, only: [:new, :create]
+
before_action :auto_sign_in_with_provider, only: [:new]
before_action :load_recaptcha
@@ -54,6 +58,14 @@ class SessionsController < Devise::SessionsController
private
+ def require_no_authentication_without_flash
+ require_no_authentication
+
+ if flash[:alert] == I18n.t('devise.failure.already_authenticated')
+ flash[:alert] = nil
+ end
+ end
+
def captcha_enabled?
request.headers[CAPTCHA_HEADER] && Gitlab::Recaptcha.enabled?
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 88ec77426d5..f1dd040515f 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -53,6 +53,7 @@ class IssuableFinder
assignee_username
author_id
author_username
+ label_name
milestone_title
my_reaction_emoji
search
diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb
index 289cb44f1e8..495c29d3e24 100644
--- a/app/helpers/broadcast_messages_helper.rb
+++ b/app/helpers/broadcast_messages_helper.rb
@@ -4,7 +4,7 @@ module BroadcastMessagesHelper
def broadcast_message(message)
return unless message.present?
- content_tag :div, class: 'broadcast-message', style: broadcast_message_style(message) do
+ content_tag :div, dir: 'auto', class: 'broadcast-message', style: broadcast_message_style(message) do
icon('bullhorn') << ' ' << render_broadcast_message(message)
end
end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index a07c3f90a91..76300e791e6 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -13,9 +13,7 @@ module LabelsHelper
# Link to a Label
#
- # label - Label object to link to
- # subject - Project/Group object which will be used as the context for the
- # label's link. If omitted, defaults to the label's own group/project.
+ # label - LabelPresenter object to link to
# type - The type of item the link will point to (:issue or
# :merge_request). If omitted, defaults to :issue.
# block - An optional block that will be passed to `link_to`, forming the
@@ -40,8 +38,8 @@ module LabelsHelper
# link_to_label(label) { "My Custom Label Text" }
#
# Returns a String
- def link_to_label(label, subject: nil, type: :issue, tooltip: true, css_class: nil, &block)
- link = label_filter_path(subject || label.subject, label, type: type)
+ def link_to_label(label, type: :issue, tooltip: true, css_class: nil, &block)
+ link = label.filter_path(type: type)
if block_given?
link_to link, class: css_class, &block
@@ -50,34 +48,6 @@ module LabelsHelper
end
end
- def label_filter_path(subject, label, type: :issue)
- case subject
- when Group
- send("#{type.to_s.pluralize}_group_path", # rubocop:disable GitlabSecurity/PublicSend
- subject,
- label_name: [label.name])
- when Project
- send("namespace_project_#{type.to_s.pluralize}_path", # rubocop:disable GitlabSecurity/PublicSend
- subject.namespace,
- subject,
- label_name: [label.name])
- end
- end
-
- def edit_label_path(label)
- case label
- when GroupLabel then edit_group_label_path(label.group, label)
- when ProjectLabel then edit_project_label_path(label.project, label)
- end
- end
-
- def destroy_label_path(label)
- case label
- when GroupLabel then group_label_path(label.group, label)
- when ProjectLabel then project_label_path(label.project, label)
- end
- end
-
def render_label(label, tooltip: true, link: nil, css: nil)
# if scoped label is used then EE wraps label tag with scoped label
# doc link
@@ -168,10 +138,6 @@ module LabelsHelper
end
end
- def can_subscribe_to_label_in_different_levels?(label)
- defined?(@project) && label.is_a?(GroupLabel)
- end
-
def label_subscription_status(label, project)
return 'group-level' if label.subscribed?(current_user)
return 'project-level' if label.subscribed?(current_user, project)
@@ -241,8 +207,8 @@ module LabelsHelper
"#{action} at #{level} level"
end
- def labels_sorted_by_title(labels)
- labels.sort_by(&:title)
+ def presented_labels_sorted_by_title(labels, subject)
+ labels.sort_by(&:title).map { |label| label.present(issuable_subject: subject) }
end
def label_dropdown_data(project, opts = {})
@@ -276,6 +242,10 @@ module LabelsHelper
klass.new(hash.slice(:color, :description, :title, :group_id, :project_id))
end
+ def issuable_types
+ ['issues', 'merge requests']
+ end
+
# Required for Banzai::Filter::LabelReferenceFilter
module_function :render_colored_label, :text_color_for_bg, :escape_once, :label_tooltip_title
end
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index be4fc2531ae..ad77f99fe44 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -83,7 +83,8 @@ module MarkupHelper
text = sanitize(
text,
tags: tags,
- attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style', 'data-src', 'data-name', 'data-unicode-version']
+ attributes: Rails::Html::WhiteListSanitizer.allowed_attributes +
+ %w(style data-src data-name data-unicode-version data-iid data-project-path data-mr-title)
)
# since <img> tags are stripped, this can leave empty <a> tags hanging around
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 7c836c6f95c..f9cf398556d 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -56,7 +56,7 @@ module Ci
update_project_statistics stat: :build_artifacts_size
- after_save :update_file_store, if: :file_changed?
+ after_save :update_file_store, if: :saved_change_to_file?
scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index b8edaf82c3d..bbd21eb0e78 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -642,6 +642,7 @@ module Ci
variables.append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message.to_s)
variables.append(key: 'CI_COMMIT_TITLE', value: git_commit_full_title.to_s)
variables.append(key: 'CI_COMMIT_DESCRIPTION', value: git_commit_description.to_s)
+ variables.append(key: 'CI_COMMIT_REF_PROTECTED', value: (!!protected_ref?).to_s)
if merge_request_event? && merge_request
variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', value: source_sha.to_s)
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index 2ae141190a8..a806367a49b 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -230,7 +230,7 @@ module Clusters
end
def update_kubernetes_namespace
- return unless namespace_changed?
+ return unless saved_change_to_namespace?
run_after_commit do
ClusterConfigureWorker.perform_async(cluster_id)
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 28ea51d6769..f90cd1ea690 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -15,7 +15,7 @@ module CacheMarkdownField
# Increment this number every time the renderer changes its output
CACHE_COMMONMARK_VERSION_START = 10
- CACHE_COMMONMARK_VERSION = 15
+ CACHE_COMMONMARK_VERSION = 16
# changes to these attributes cause the cache to be invalidates
INVALIDATED_BY = %w[author project].freeze
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
index 498996f4f80..1cbe27ad03a 100644
--- a/app/models/concerns/storage/legacy_namespace.rb
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -13,8 +13,8 @@ module Storage
raise Gitlab::UpdatePathError.new("Namespace #{name} (#{id}) cannot be moved because at least one project (e.g. #{proj_with_tags.name} (#{proj_with_tags.id})) has tags in container registry")
end
- parent_was = if parent_changed? && parent_id_was.present?
- Namespace.find(parent_id_was) # raise NotFound early if needed
+ parent_was = if parent_changed? && parent_id_before_last_save.present?
+ Namespace.find(parent_id_before_last_save) # raise NotFound early if needed
end
move_repositories
@@ -104,11 +104,5 @@ module Storage
end
end
end
-
- def remove_legacy_exports!
- legacy_export_path = File.join(Gitlab::ImportExport.storage_path, full_path_was)
-
- FileUtils.rm_rf(legacy_export_path)
- end
end
end
diff --git a/app/models/concerns/update_project_statistics.rb b/app/models/concerns/update_project_statistics.rb
index bffc711c886..67e1f0ec930 100644
--- a/app/models/concerns/update_project_statistics.rb
+++ b/app/models/concerns/update_project_statistics.rb
@@ -39,12 +39,12 @@ module UpdateProjectStatistics
end
def update_project_statistics_attribute_changed?
- attribute_changed?(self.class.statistic_attribute)
+ saved_change_to_attribute?(self.class.statistic_attribute)
end
def update_project_statistics_after_save
attr = self.class.statistic_attribute
- delta = read_attribute(attr).to_i - attribute_was(attr).to_i
+ delta = read_attribute(attr).to_i - attribute_before_last_save(attr).to_i
update_project_statistics(delta)
end
diff --git a/app/models/global_label.rb b/app/models/global_label.rb
index 572cb12b26a..7c020dd3b3d 100644
--- a/app/models/global_label.rb
+++ b/app/models/global_label.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class GlobalLabel
+ include Presentable
+
attr_accessor :title, :labels
alias_attribute :name, :title
@@ -23,4 +25,8 @@ class GlobalLabel
@labels = labels
@first_label = labels.find { |lbl| lbl.description.present? } || labels.first
end
+
+ def present(attributes)
+ super(attributes.merge(presenter_class: ::LabelPresenter))
+ end
end
diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb
index fd17745b035..59f5a7703e2 100644
--- a/app/models/global_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -8,7 +8,9 @@ class GlobalMilestone
attr_reader :milestone
alias_attribute :name, :title
- delegate :title, :state, :due_date, :start_date, :participants, :project, :group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title, :milestoneish_id, to: :milestone
+ delegate :title, :state, :due_date, :start_date, :participants, :project,
+ :group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title,
+ :milestoneish_id, :parent, to: :milestone
def to_hash
{
diff --git a/app/models/group.rb b/app/models/group.rb
index 8bc9b75f0a9..53331a19776 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -61,7 +61,7 @@ class Group < Namespace
after_create :post_create_hook
after_destroy :post_destroy_hook
after_save :update_two_factor_requirement
- after_update :path_changed_hook, if: :path_changed?
+ after_update :path_changed_hook, if: :saved_change_to_path?
class << self
def sort_by_attribute(method)
@@ -411,7 +411,7 @@ class Group < Namespace
private
def update_two_factor_requirement
- return unless require_two_factor_authentication_changed? || two_factor_grace_period_changed?
+ return unless saved_change_to_require_two_factor_authentication? || saved_change_to_two_factor_grace_period?
users.find_each(&:update_two_factor_requirement)
end
diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb
index 401b94d36e5..237401899db 100644
--- a/app/models/internal_id.rb
+++ b/app/models/internal_id.rb
@@ -87,12 +87,16 @@ class InternalId < ApplicationRecord
end
def available?
- @available_flag ||= ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION # rubocop:disable Gitlab/PredicateMemoization
+ return true unless Rails.env.test?
+
+ Gitlab::SafeRequestStore.fetch(:internal_ids_available_flag) do
+ ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION
+ end
end
# Flushes cached information about schema
def reset_column_information
- @available_flag = nil
+ Gitlab::SafeRequestStore[:internal_ids_available_flag] = nil
super
end
end
diff --git a/app/models/label.rb b/app/models/label.rb
index c7fff0d393e..e9085e8bd25 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -8,6 +8,7 @@ class Label < ApplicationRecord
include OptionallySearch
include Sortable
include FromUnion
+ include Presentable
cache_markdown_field :description, pipeline: :single_line
@@ -233,6 +234,10 @@ class Label < ApplicationRecord
attributes
end
+ def present(attributes)
+ super(attributes.merge(presenter_class: ::LabelPresenter))
+ end
+
private
def issues_count(user, params = {})
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index e1aac691a64..5245dbc8d15 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -13,7 +13,7 @@ class LfsObject < ApplicationRecord
mount_uploader :file, LfsObjectUploader
- after_save :update_file_store, if: :file_changed?
+ after_save :update_file_store, if: :saved_change_to_file?
def update_file_store
# The file.object_store is set during `uploader.store!`
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 510f856087d..b266c61f002 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -55,7 +55,7 @@ class GroupMember < Member
end
def post_update_hook
- if access_level_changed?
+ if saved_change_to_access_level?
run_after_commit { notification_service.update_group_member(self) }
end
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 5372c6084f4..c64e2669b6a 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -111,7 +111,7 @@ class ProjectMember < Member
end
def post_update_hook
- if access_level_changed?
+ if saved_change_to_access_level?
run_after_commit { notification_service.update_project_member(self) }
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 251a7ff41f5..a5b62659b24 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -698,7 +698,7 @@ class MergeRequest < ApplicationRecord
end
def reload_diff_if_branch_changed
- if (source_branch_changed? || target_branch_changed?) &&
+ if (saved_change_to_source_branch? || saved_change_to_target_branch?) &&
(source_branch_head && target_branch_head)
reload_diff
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 8638c5a9c53..7228aab2c2e 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -50,6 +50,8 @@ class Namespace < ApplicationRecord
validate :nesting_level_allowed
+ validates_associated :runners
+
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :avatar_url, to: :owner, allow_nil: true
@@ -57,7 +59,7 @@ class Namespace < ApplicationRecord
before_create :sync_share_with_group_lock_with_parent
before_update :sync_share_with_group_lock_with_parent, if: :parent_changed?
- after_update :force_share_with_group_lock_on_descendants, if: -> { share_with_group_lock_changed? && share_with_group_lock? }
+ after_update :force_share_with_group_lock_on_descendants, if: -> { saved_change_to_share_with_group_lock? && share_with_group_lock? }
# Legacy Storage specific hooks
@@ -292,7 +294,7 @@ class Namespace < ApplicationRecord
private
def path_or_parent_changed?
- path_changed? || parent_changed?
+ saved_change_to_path? || saved_change_to_parent_id?
end
def refresh_access_of_projects_invited_groups
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index 82901ceec01..d73b2889f30 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -147,20 +147,20 @@ class PagesDomain < ApplicationRecord
# rubocop: enable CodeReuse/ServiceClass
def pages_config_changed?
- project_id_changed? ||
- domain_changed? ||
- certificate_changed? ||
- key_changed? ||
+ saved_change_to_project_id? ||
+ saved_change_to_domain? ||
+ saved_change_to_certificate? ||
+ saved_change_to_key? ||
became_enabled? ||
became_disabled?
end
def became_enabled?
- enabled_until.present? && !enabled_until_was.present?
+ enabled_until.present? && !enabled_until_before_last_save.present?
end
def became_disabled?
- !enabled_until.present? && enabled_until_was.present?
+ !enabled_until.present? && enabled_until_before_last_save.present?
end
def validate_matching_key
diff --git a/app/models/project.rb b/app/models/project.rb
index b7a10b3547a..626ff9e1389 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -90,7 +90,7 @@ class Project < ApplicationRecord
before_save :ensure_runners_token
- after_save :update_project_statistics, if: :namespace_id_changed?
+ after_save :update_project_statistics, if: :saved_change_to_namespace_id?
after_save :create_import_state, if: ->(project) { project.import? && project.import_state.nil? }
@@ -116,7 +116,7 @@ class Project < ApplicationRecord
after_initialize :use_hashed_storage
after_create :check_repository_absence!
after_create :ensure_storage_path_exists
- after_save :ensure_storage_path_exists, if: :namespace_id_changed?
+ after_save :ensure_storage_path_exists, if: :saved_change_to_namespace_id?
acts_as_ordered_taggable
@@ -1430,7 +1430,7 @@ class Project < ApplicationRecord
# update visibility_level of forks
def update_forks_visibility_level
- return unless visibility_level < visibility_level_was
+ return unless visibility_level < visibility_level_before_last_save
forks.each do |forked_project|
if forked_project.visibility_level > visibility_level
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 23ddd708396..c91add6439f 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -82,17 +82,25 @@ class ProjectWiki
end
def empty?
- pages(limit: 1).empty?
+ list_pages(limit: 1).empty?
end
+ # Lists wiki pages of the repository.
+ #
+ # limit - max number of pages returned by the method.
+ # sort - criterion by which the pages are sorted.
+ # direction - order of the sorted pages.
+ # load_content - option, which specifies whether the content inside the page
+ # will be loaded.
+ #
# Returns an Array of GitLab WikiPage instances or an
# empty Array if this Wiki has no pages.
- def pages(limit: 0, sort: nil, direction: DIRECTION_ASC)
- sort ||= TITLE_ORDER
- direction_desc = direction == DIRECTION_DESC
-
- wiki.pages(
- limit: limit, sort: sort, direction_desc: direction_desc
+ def list_pages(limit: 0, sort: nil, direction: DIRECTION_ASC, load_content: false)
+ wiki.list_pages(
+ limit: limit,
+ sort: sort,
+ direction_desc: direction == DIRECTION_DESC,
+ load_content: load_content
).map do |page|
WikiPage.new(self, page, true)
end
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index b2fd5394a03..535f772b6a1 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -248,7 +248,7 @@ class RemoteMirror < ApplicationRecord
# Before adding a new remote we have to delete the data from
# the previous remote name
- prev_remote_name = remote_name_was || fallback_remote_name
+ prev_remote_name = remote_name_before_last_save || fallback_remote_name
run_after_commit do
project.repository.async_remove_remote(prev_remote_name)
end
diff --git a/app/models/route.rb b/app/models/route.rb
index 7e3db54d4fe..91ea2966013 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -14,26 +14,26 @@ class Route < ApplicationRecord
before_validation :delete_conflicting_orphaned_routes
after_create :delete_conflicting_redirects
- after_update :delete_conflicting_redirects, if: :path_changed?
+ after_update :delete_conflicting_redirects, if: :saved_change_to_path?
after_update :create_redirect_for_old_path
after_update :rename_descendants
scope :inside_path, -> (path) { where('routes.path LIKE ?', "#{sanitize_sql_like(path)}/%") }
def rename_descendants
- return unless path_changed? || name_changed?
+ return unless saved_change_to_path? || saved_change_to_name?
- descendant_routes = self.class.inside_path(path_was)
+ descendant_routes = self.class.inside_path(path_before_last_save)
descendant_routes.each do |route|
attributes = {}
- if path_changed? && route.path.present?
- attributes[:path] = route.path.sub(path_was, path)
+ if saved_change_to_path? && route.path.present?
+ attributes[:path] = route.path.sub(path_before_last_save, path)
end
- if name_changed? && name_was.present? && route.name.present?
- attributes[:name] = route.name.sub(name_was, name)
+ if saved_change_to_name? && name_before_last_save.present? && route.name.present?
+ attributes[:name] = route.name.sub(name_before_last_save, name)
end
if attributes.present?
@@ -65,7 +65,7 @@ class Route < ApplicationRecord
private
def create_redirect_for_old_path
- create_redirect(path_was) if path_changed?
+ create_redirect(path_before_last_save) if saved_change_to_path?
end
def delete_conflicting_orphaned_routes
diff --git a/app/models/user.rb b/app/models/user.rb
index 551eb58a4de..43039f3760e 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -194,7 +194,7 @@ class User < ApplicationRecord
before_validation :ensure_namespace_correct
before_save :ensure_namespace_correct # in case validation is skipped
after_validation :set_username_errors
- after_update :username_changed_hook, if: :username_changed?
+ after_update :username_changed_hook, if: :saved_change_to_username?
after_destroy :post_destroy_hook
after_destroy :remove_key_cache
after_commit(on: :update) do
diff --git a/app/presenters/label_presenter.rb b/app/presenters/label_presenter.rb
new file mode 100644
index 00000000000..5227ef353c3
--- /dev/null
+++ b/app/presenters/label_presenter.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+class LabelPresenter < Gitlab::View::Presenter::Delegated
+ presents :label
+
+ def edit_path
+ case label
+ when GroupLabel then edit_group_label_path(label.group, label)
+ when ProjectLabel then edit_project_label_path(label.project, label)
+ end
+ end
+
+ def destroy_path
+ case label
+ when GroupLabel then group_label_path(label.group, label)
+ when ProjectLabel then project_label_path(label.project, label)
+ end
+ end
+
+ def filter_path(type: :issue)
+ case context_subject
+ when Group
+ send("#{type.to_s.pluralize}_group_path", # rubocop:disable GitlabSecurity/PublicSend
+ context_subject,
+ label_name: [label.name])
+ when Project
+ send("namespace_project_#{type.to_s.pluralize}_path", # rubocop:disable GitlabSecurity/PublicSend
+ context_subject.namespace,
+ context_subject,
+ label_name: [label.name])
+ end
+ end
+
+ def can_subscribe_to_label_in_different_levels?
+ issuable_subject.is_a?(Project) && label.is_a?(GroupLabel)
+ end
+
+ private
+
+ def context_subject
+ issuable_subject || label.try(:subject)
+ end
+end
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index 3adf8a0c1a1..3f0aedfbfb2 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -3,7 +3,7 @@
require 'securerandom'
# Compare 2 refs for one repo or between repositories
-# and return Gitlab::Git::Compare object that responds to commits and diffs
+# and return Compare object that responds to commits and diffs
class CompareService
attr_reader :start_project, :start_ref_name
@@ -15,7 +15,7 @@ class CompareService
def execute(target_project, target_ref, base_sha: nil, straight: false)
raw_compare = target_project.repository.compare_source_branch(target_ref, start_project.repository, start_ref_name, straight: straight)
- return unless raw_compare
+ return unless raw_compare && raw_compare.base && raw_compare.head
Compare.new(raw_compare,
target_project,
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index 4c3ac19f754..fd41ce54486 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -6,27 +6,25 @@ class DeleteBranchService < BaseService
branch = repository.find_branch(branch_name)
unless current_user.can?(:push_code, project)
- return error('You dont have push access to repo', 405)
+ return ServiceResponse.error(
+ message: 'You dont have push access to repo',
+ http_status: 405)
end
unless branch
- return error('No such branch', 404)
+ return ServiceResponse.error(
+ message: 'No such branch',
+ http_status: 404)
end
if repository.rm_branch(current_user, branch_name)
- success('Branch was deleted')
+ ServiceResponse.success(message: 'Branch was deleted')
else
- error('Failed to remove branch')
+ ServiceResponse.error(
+ message: 'Failed to remove branch',
+ http_status: 400)
end
rescue Gitlab::Git::PreReceiveError => ex
- error(ex.message)
- end
-
- def error(message, return_code = 400)
- super(message).merge(return_code: return_code)
- end
-
- def success(message)
- super().merge(message: message)
+ ServiceResponse.error(message: ex.message, http_status: 400)
end
end
diff --git a/app/services/projects/propagate_service_template.rb b/app/services/projects/propagate_service_template.rb
index 633a263af7b..a2f36d2bd1b 100644
--- a/app/services/projects/propagate_service_template.rb
+++ b/app/services/projects/propagate_service_template.rb
@@ -80,7 +80,7 @@ module Projects
value = value.is_a?(Hash) ? value.to_json : value
service_hash[ActiveRecord::Base.connection.quote_column_name(key)] =
- ActiveRecord::Base.sanitize(value)
+ ActiveRecord::Base.connection.quote(value)
end
end
end
diff --git a/app/services/service_response.rb b/app/services/service_response.rb
new file mode 100644
index 00000000000..1de30e68d87
--- /dev/null
+++ b/app/services/service_response.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class ServiceResponse
+ def self.success(message: nil)
+ new(status: :success, message: message)
+ end
+
+ def self.error(message:, http_status: nil)
+ new(status: :error, message: message, http_status: http_status)
+ end
+
+ attr_reader :status, :message, :http_status
+
+ def initialize(status:, message: nil, http_status: nil)
+ self.status = status
+ self.message = message
+ self.http_status = http_status
+ end
+
+ def success?
+ status == :success
+ end
+
+ def error?
+ status == :error
+ end
+
+ private
+
+ attr_writer :status, :message, :http_status
+end
diff --git a/app/services/test_hooks/project_service.rb b/app/services/test_hooks/project_service.rb
index 6607f5b2418..a71278e8b8b 100644
--- a/app/services/test_hooks/project_service.rb
+++ b/app/services/test_hooks/project_service.rb
@@ -56,7 +56,7 @@ module TestHooks
end
def wiki_page_events_data
- page = project.wiki.pages.first
+ page = project.wiki.list_pages(limit: 1).first
if !project.wiki_enabled? || page.blank?
throw(:validation_error, s_('TestHooks|Ensure the wiki is enabled and has pages.'))
end
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index c465d9f51d6..46beca0465e 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -14,6 +14,7 @@
.col-sm-10
= f.text_area :message, class: "form-control js-autosize",
required: true,
+ dir: 'auto',
data: { preview_path: preview_admin_broadcast_messages_path }
.form-group.row.js-toggle-colors-container
.col-sm-10.offset-sm-2
diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml
index dbb7224f5f9..6d934654c5d 100644
--- a/app/views/admin/labels/_label.html.haml
+++ b/app/views/admin/labels/_label.html.haml
@@ -1,5 +1,5 @@
%li.label-list-item{ id: dom_id(label) }
- = render "shared/label_row", label: label
+ = render "shared/label_row", label: label.present(issuable_subject: nil)
.label-actions-list
= link_to edit_admin_label_path(label), class: 'btn btn-transparent label-action has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
= sprite_icon('pencil')
diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml
index 96d6553a2ac..b02fdb4b638 100644
--- a/app/views/events/event/_common.html.haml
+++ b/app/views/events/event/_common.html.haml
@@ -11,7 +11,8 @@
= link_to [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip event-target-link append-right-4', title: event.target_title do
= event.target.reference_link_text
- unless event.milestone?
- %span.event-target-title.append-right-4= "&quot;".html_safe + event.target.title + "&quot".html_safe
+ %span.event-target-title.append-right-4{ dir: "auto" }
+ = "&quot;".html_safe + event.target.title + "&quot".html_safe
- else
%span.event-type.d-inline-block.append-right-4{ class: event.action_name }
= event_action_name(event)
diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml
index 90ed8e41d32..7e2103287f7 100644
--- a/app/views/events/event/_note.html.haml
+++ b/app/views/events/event/_note.html.haml
@@ -7,7 +7,8 @@
%span.event-type.d-inline-block.append-right-4{ class: event.action_name }
= event.action_name
= event_note_title_html(event)
- %span.event-target-title.append-right-4= "&quot;".html_safe + event.target.title + "&quot".html_safe
+ %span.event-target-title.append-right-4{ dir: "auto" }
+ = "&quot;".html_safe + event.target.title + "&quot".html_safe
= render "events/event_scope", event: event
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index 5cf3193bc62..a8358704b03 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -1,7 +1,6 @@
- @no_container = true
-- page_title "Labels"
+- page_title 'Labels'
- can_admin_label = can?(current_user, :admin_label, @group)
-- issuables = ['issues', 'merge requests']
- search = params[:search]
- subscribed = params[:subscribed]
- labels_or_filters = @labels.exists? || search.present? || subscribed.present?
@@ -14,11 +13,11 @@
.labels-container.prepend-top-5
- if @labels.any?
.text-muted
- = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence }
+ = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuable_types.to_sentence }
.other-labels
%h5= _('Labels')
%ul.content-list.manage-labels-list.js-other-labels
- = render partial: 'shared/label', subject: @group, collection: @labels, as: :label, locals: { use_label_priority: false }
+ = render partial: 'shared/label', collection: @labels, as: :label, locals: { use_label_priority: false, subject: @group }
= paginate @labels, theme: 'gitlab'
- elsif search.present?
.nothing-here-block
diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml
index a88b04eccbb..c4670869c93 100644
--- a/app/views/import/gitea/new.html.haml
+++ b/app/views/import/gitea/new.html.haml
@@ -2,18 +2,18 @@
- header_title _("Projects"), root_path
%h3.page-title
- = custom_icon('go_logo')
+ = custom_icon('gitea_logo')
= _('Import Projects from Gitea')
%p
- - link_to_personal_token = link_to(_('Personal Access Token'), 'https://github.com/gogits/go-gogs-client/wiki#access-token')
+ - link_to_personal_token = link_to(_('Personal Access Token'), 'https://docs.gitea.io/en-us/api-usage/#authentication-via-the-api')
= _('To get started, please enter your Gitea Host URL and a %{link_to_personal_token}.').html_safe % { link_to_personal_token: link_to_personal_token }
= form_tag personal_access_token_import_gitea_path do
.form-group.row
= label_tag :gitea_host_url, _('Gitea Host URL'), class: 'col-form-label col-sm-2'
.col-sm-4
- = text_field_tag :gitea_host_url, nil, placeholder: 'https://try.gitea.io', class: 'form-control'
+ = text_field_tag :gitea_host_url, nil, placeholder: 'https://gitea.com', class: 'form-control'
.form-group.row
= label_tag :personal_access_token, _('Personal Access Token'), class: 'col-form-label col-sm-2'
.col-sm-4
diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml
index 88244fde16b..ef0693e73c3 100644
--- a/app/views/import/gitea/status.html.haml
+++ b/app/views/import/gitea/status.html.haml
@@ -1,7 +1,7 @@
- page_title _("Gitea Import")
- header_title _("Projects"), root_path
%h3.page-title
- = custom_icon('go_logo')
+ = custom_icon('gitea_logo')
= _('Import Projects from Gitea')
= render 'import/githubish_status', provider: 'gitea'
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 50adc19f524..3ca4abddbb8 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -80,6 +80,8 @@
- deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)')
= deleted_message % { project_name: fork_source_name(@project) }
+ = render_if_exists "projects/home_mirror"
+
- if @project.badges.present?
.project-badges.mb-2
- @project.badges.each do |badge|
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 2b425f18389..9c854369c93 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -50,7 +50,7 @@
- if gitea_import_enabled?
%div
= link_to new_import_gitea_path, class: 'btn import_gitea', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "gitea" } do
- = custom_icon('go_logo')
+ = custom_icon('gitea_logo')
Gitea
- if git_import_enabled?
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index afc40ca4eab..c502b392384 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -8,6 +8,7 @@
= f.text_area attr,
class: classes,
placeholder: placeholder,
+ dir: 'auto',
data: { supports_quick_actions: supports_quick_actions,
supports_autocomplete: supports_autocomplete }
- else
diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml
index e8a89b8c6fc..b37dba8b35d 100644
--- a/app/views/projects/forks/error.html.haml
+++ b/app/views/projects/forks/error.html.haml
@@ -1,24 +1,20 @@
-- page_title "Fork project"
+- page_title _("Fork project")
- if @forked_project && !@forked_project.saved?
.alert.alert-danger.alert-block
%h4
= sprite_icon('fork', size: 16)
- Fork Error!
+ = _("Fork Error!")
%p
- You tried to fork
- = link_to_project @project
- but it failed for the following reason:
-
+ = _("You tried to fork %{link_to_the_project} but it failed for the following reason:").html_safe % { link_to_the_project: link_to_project(@project) }
- if @forked_project && @forked_project.errors.any?
%p
&ndash;
- error = @forked_project.errors.full_messages.first
- if error.include?("already been taken")
- Name has already been taken
+ = _("Name has already been taken")
- else
= error
%p
- = link_to new_project_fork_path(@project), title: "Fork", class: "btn" do
- Try to fork again
+ = link_to _("Try to fork again"), new_project_fork_path(@project), title: _("Fork"), class: "btn"
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index c63c34c4ebb..0397a7034c7 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -5,12 +5,12 @@
.nav-controls
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
- = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short',
+ = search_field_tag :filter_projects, nil, placeholder: _('Search forks'), class: 'projects-list-filter project-filter-form-field form-control input-short',
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown
%button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
- %span.light sort:
+ %span.light= _("sort:")
- if @sort.present?
= sort_options_hash[@sort]
- else
@@ -30,13 +30,12 @@
- if current_user && can?(current_user, :fork_project, @project)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
- = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-success' do
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn btn-success' do
= sprite_icon('fork', size: 12)
- %span Fork
+ %span= _('Fork')
- else
- = link_to new_project_fork_path(@project), title: "Fork project", class: 'btn btn-success' do
+ = link_to new_project_fork_path(@project), title: _("Fork project"), class: 'btn btn-success' do
= sprite_icon('fork', size: 12)
- %span Fork
-
+ %span= _('Fork')
= render 'projects', projects: @forks
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index a603b1024eb..bf03353a565 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -1,13 +1,11 @@
-- page_title "Fork project"
+- page_title _("Fork project")
.row.prepend-top-default
.col-lg-3
%h4.prepend-top-0
- Fork project
+ = _("Fork project")
%p
- A fork is a copy of a project.
- %br
- Forking a repository allows you to make changes without affecting the original project.
+ = _("A fork is a copy of a project.<br />Forking a repository allows you to make changes without affecting the original project.").html_safe
.col-lg-9
- if @namespaces.present?
.fork-thumbnail-container.js-fork-content
@@ -17,13 +15,13 @@
= render 'fork_button', namespace: namespace
- else
%strong
- No available namespaces to fork the project.
+ = _("No available namespaces to fork the project.")
%p.prepend-top-default
- You must have permission to create a project in a namespace before forking.
+ = _("You must have permission to create a project in a namespace before forking.")
.save-project-loader.hide.js-fork-content
%h2.text-center
= icon('spinner spin')
- Forking repository
+ = _("Forking repository")
%p.text-center
- Please wait a moment, this page will automatically refresh when ready.
+ = _("Please wait a moment, this page will automatically refresh when ready.")
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 377b2a6d8d9..0d8d7123a01 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -6,7 +6,7 @@
.issuable-info-container
.issuable-main-info
.issue-title.title
- %span.issue-title-text
+ %span.issue-title-text{ dir: "auto" }
- if issue.confidential?
%span.has-tooltip{ title: _('Confidential') }
= confidential_icon(issue)
@@ -36,8 +36,8 @@
= issue.due_date.to_s(:medium)
- if issue.labels.any?
&nbsp;
- - labels_sorted_by_title(issue.labels).each do |label|
- = link_to_label(label, subject: issue.project, css_class: 'label-link')
+ - presented_labels_sorted_by_title(issue.labels, issue.project).each do |label|
+ = link_to_label(label, css_class: 'label-link')
.issuable-meta
%ul.controls
diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml
index 9a081a42b6f..d1601d7fd10 100644
--- a/app/views/projects/issues/new.html.haml
+++ b/app/views/projects/issues/new.html.haml
@@ -1,9 +1,8 @@
-- add_to_breadcrumbs "Issues", project_issues_path(@project)
-- breadcrumb_title "New"
-- page_title "New Issue"
+- add_to_breadcrumbs _("Issues"), project_issues_path(@project)
+- breadcrumb_title _("New")
+- page_title _("New Issue")
-%h3.page-title
- New Issue
+%h3.page-title= _("New Issue")
%hr
= render "form"
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 4bf1d8702af..0bf664d5b66 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -1,7 +1,7 @@
- @content_class = "limit-container-width" unless fluid_layout
-- add_to_breadcrumbs "Issues", project_issues_path(@project)
+- add_to_breadcrumbs _("Issues"), project_issues_path(@project)
- breadcrumb_title @issue.to_reference
-- page_title "#{@issue.title} (#{@issue.to_reference})", "Issues"
+- page_title "#{@issue.title} (#{@issue.to_reference})", _("Issues")
- page_description @issue.description
- page_card_attributes @issue.card_attributes
@@ -77,6 +77,8 @@
= edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue-edited-ago js-issue-edited-ago')
+ = render_if_exists 'projects/issues/related_issues'
+
#js-related-merge-requests{ data: { endpoint: expose_url(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid)), project_namespace: @project.namespace.path, project_path: @project.path } }
- if can?(current_user, :download_code, @project)
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 5d73d832170..511d7a82d1b 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -25,7 +25,7 @@
#js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty? && search.blank?}" }
= render 'shared/empty_states/priority_labels'
- if @prioritized_labels.present?
- = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label, locals: { force_priority: true }
+ = render partial: 'shared/label', collection: @prioritized_labels, as: :label, locals: { force_priority: true, subject: @project }
- elsif search.present?
.nothing-here-block
= _('No prioritised labels with such name or description')
@@ -34,7 +34,7 @@
.other-labels
%h5{ class: ('hide' if hide) }= _('Other Labels')
.content-list.manage-labels-list.js-other-labels
- = render partial: 'shared/label', subject: @project, collection: @labels, as: :label
+ = render partial: 'shared/label', collection: @labels, as: :label, locals: { subject: @project }
= paginate @labels, theme: 'gitlab'
- elsif search.present?
.other-labels
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 47c8e3d73f5..67e5e4ca62d 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -34,8 +34,8 @@
= merge_request.target_branch
- if merge_request.labels.any?
&nbsp;
- - labels_sorted_by_title(merge_request.labels).each do |label|
- = link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link')
+ - presented_labels_sorted_by_title(merge_request.labels, merge_request.project).each do |label|
+ = link_to_label(label, type: :merge_request, css_class: 'label-link')
.issuable-meta
%ul.controls
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 79c586eef73..05aeb5d972b 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -37,21 +37,21 @@
%ul.merge-request-tabs.nav-tabs.nav.nav-links.scrolling-tabs
%li.notes-tab.qa-notes-tab
= tab_link_for @merge_request, :show, force_link: @commit.present? do
- Discussion
+ = _("Discussion")
%span.badge.badge-pill= @merge_request.related_notes.user.count
- if @merge_request.source_project
%li.commits-tab
= tab_link_for @merge_request, :commits do
- Commits
+ = _("Commits")
%span.badge.badge-pill= @commits_count
- if @pipelines.any?
%li.pipelines-tab
= tab_link_for @merge_request, :pipelines do
- Pipelines
+ = _("Pipelines")
%span.badge.badge-pill.js-pipelines-mr-count= @pipelines.size
%li.diffs-tab.qa-diffs-tab
= tab_link_for @merge_request, :diffs do
- Changes
+ = _("Changes")
%span.badge.badge-pill= @merge_request.diff_size
.d-inline-flex.flex-wrap
#js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(@merge_request),
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
index c031815200b..a1ec2c887c2 100644
--- a/app/views/projects/mirrors/_mirror_repos.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -11,7 +11,7 @@
= link_to _('Read more'), help_page_path('workflow/repository_mirroring'), target: '_blank'
.settings-content
- = form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'false', data: mirrors_form_data_attributes } do |f|
+ = form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f|
.panel.panel-default
.panel-heading
%h3.panel-title= _('Mirror a repository')
@@ -20,7 +20,7 @@
.form-group.has-feedback
= label_tag :url, _('Git repository URL'), class: 'label-light'
- = text_field_tag :url, nil, class: 'form-control js-mirror-url js-repo-url qa-mirror-repository-url-input', placeholder: _('Input your repository URL'), required: true, pattern: "(#{protocols}):\/\/.+"
+ = text_field_tag :url, nil, class: 'form-control js-mirror-url js-repo-url qa-mirror-repository-url-input', placeholder: _('Input your repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", autocomplete: 'new-password'
= render 'projects/mirrors/instructions'
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 5b25a67bc87..8ae2807729b 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -21,10 +21,9 @@
- if @scope == 'projects'
.term
= render 'shared/projects/list', projects: @search_objects, pipeline_status: false
- - elsif %w[blobs wiki_blobs].include?(@scope)
- = render partial: 'search/results/blob', collection: @search_objects, locals: { projects: blob_projects(@search_objects) }
- else
- = render partial: "search/results/#{@scope.singularize}", collection: @search_objects
+ - locals = { projects: blob_projects(@search_objects) } if %w[blobs wiki_blobs].include?(@scope)
+ = render partial: "search/results/#{@scope.singularize}", collection: @search_objects, locals: locals
- if @scope != 'projects'
= paginate_collection(@search_objects)
diff --git a/app/views/shared/_delete_label_modal.html.haml b/app/views/shared/_delete_label_modal.html.haml
index dbd3bbb43af..6bd8cadd7d9 100644
--- a/app/views/shared/_delete_label_modal.html.haml
+++ b/app/views/shared/_delete_label_modal.html.haml
@@ -9,13 +9,13 @@
.modal-body
%p
%strong= label.name
- %span will be permanently deleted from #{label.is_a?(ProjectLabel)? label.project.name : label.group.name}. This cannot be undone.
+ %span will be permanently deleted from #{label.subject.name}. This cannot be undone.
.modal-footer
%a{ href: '#', data: { dismiss: 'modal' }, class: 'btn btn-default' } Cancel
= link_to 'Delete label',
- destroy_label_path(label),
+ label.destroy_path,
title: 'Delete',
method: :delete,
class: 'btn btn-remove'
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index 21ea188d7b3..2b4a24a001f 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -1,13 +1,13 @@
+- label = label.present(issuable_subject: local_assigns[:subject])
- label_css_id = dom_id(label)
- status = label_subscription_status(label, @project).inquiry if current_user
-- subject = local_assigns[:subject]
- use_label_priority = local_assigns.fetch(:use_label_priority, false)
- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority.present? : false)
- toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user
- tooltip_title = label_status_tooltip(label, status) if status
%li.label-list-item{ id: label_css_id, data: { id: label.id } }
- = render "shared/label_row", label: label, subject: subject, force_priority: force_priority
+ = render "shared/label_row", label: label, force_priority: force_priority
%ul.label-actions-list
- if @project
%li.inline
@@ -21,7 +21,7 @@
= sprite_icon('star')
- if can?(current_user, :admin_label, label)
%li.inline
- = link_to edit_label_path(label), class: 'btn btn-transparent label-action edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
+ = link_to label.edit_path, class: 'btn btn-transparent label-action edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
= sprite_icon('pencil')
- if can?(current_user, :admin_label, label)
%li.inline
@@ -48,7 +48,7 @@
%button.text-danger.remove-row{ type: 'button' }= _('Delete')
- if current_user
%li.inline.label-subscription
- - if can_subscribe_to_label_in_different_levels?(label)
+ - if label.can_subscribe_to_label_in_different_levels?
%button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
%span= _('Unsubscribe')
.dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) }
diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml
index 9d1648fbf70..a1aab2e6a08 100644
--- a/app/views/shared/_label_row.html.haml
+++ b/app/views/shared/_label_row.html.haml
@@ -1,4 +1,3 @@
-- subject = local_assigns[:subject]
- force_priority = local_assigns.fetch(:force_priority, false)
- subject_or_group_defined = defined?(@project) || defined?(@group)
- show_label_issues_link = subject_or_group_defined && show_label_issuables_link?(label, :issues, project: @project)
@@ -14,11 +13,11 @@
%ul.label-links
- if show_label_issues_link
%li.label-link-item.inline
- = link_to_label(label, subject: subject) { 'Issues' }
+ = link_to_label(label) { 'Issues' }
- if show_label_merge_requests_link
&middot;
%li.label-link-item.inline
- = link_to_label(label, subject: subject, type: :merge_request) { _('Merge requests') }
+ = link_to_label(label, type: :merge_request) { _('Merge requests') }
- if force_priority
&middot;
%li.label-link-item.priority-badge.js-priority-badge.inline.prepend-left-10
diff --git a/app/views/shared/_sidebar_toggle_button.html.haml b/app/views/shared/_sidebar_toggle_button.html.haml
index 2530db986e0..d90a6d43761 100644
--- a/app/views/shared/_sidebar_toggle_button.html.haml
+++ b/app/views/shared/_sidebar_toggle_button.html.haml
@@ -1,8 +1,8 @@
%a.toggle-sidebar-button.js-toggle-sidebar{ role: "button", type: "button", title: "Toggle sidebar" }
= sprite_icon('angle-double-left', css_class: 'icon-angle-double-left')
= sprite_icon('angle-double-right', css_class: 'icon-angle-double-right')
- %span.collapse-text Collapse sidebar
+ %span.collapse-text= _("Collapse sidebar")
= button_tag class: 'close-nav-button', type: 'button' do
= sprite_icon('close', size: 16)
- %span.collapse-text Close sidebar
+ %span.collapse-text= _("Close sidebar")
diff --git a/app/views/shared/icons/_gitea_logo.svg.erb b/app/views/shared/icons/_gitea_logo.svg.erb
new file mode 100644
index 00000000000..c8ddbc5535e
--- /dev/null
+++ b/app/views/shared/icons/_gitea_logo.svg.erb
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!-- Created with Inkscape (http://www.inkscape.org/) --><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="<%= size %>" height="<%= size %>" viewBox="0 0 135.46667 135.46667" version="1.1" id="svg8" sodipodi:docname="logo.svg" inkscape:version="0.92.1 r15371" inkscape:export-filename="" inkscape:export-xdpi="48.000004" inkscape:export-ydpi="48.000004" style="zoom: 1;"><defs id="defs2"></defs><sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="0.70710678" inkscape:cx="418.13805" inkscape:cy="177.57445" inkscape:document-units="mm" inkscape:current-layer="layer2" showgrid="false" units="px" width="256px" showguides="false" inkscape:window-width="1920" inkscape:window-height="1137" inkscape:window-x="1912" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:pagecheckerboard="false" inkscape:measure-start="283.373,243.952" inkscape:measure-end="290.267,236.527"><sodipodi:guide position="0,0" orientation="0,512" id="guide3699" inkscape:locked="false"></sodipodi:guide><sodipodi:guide position="135.46667,0" orientation="-512,0" id="guide3701" inkscape:locked="false"></sodipodi:guide><sodipodi:guide position="135.46667,135.46667" orientation="0,-512" id="guide3703" inkscape:locked="false"></sodipodi:guide><sodipodi:guide position="0,135.46667" orientation="512,0" id="guide3705" inkscape:locked="false"></sodipodi:guide></sodipodi:namedview><metadata id="metadata5"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"></dc:type><dc:title></dc:title></cc:Work></rdf:RDF></metadata><g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-161.53334)" style="display:inline"><path d="M27.709937,195.15095 c-9.546573,-0.0272 -22.3392732,6.79805 -21.6317552,23.90397 c1.105534,26.72889 25.4565952,29.20839 35.1916502,29.42301 c1.068023,5.01357 12.521798,22.30563 21.001818,23.21667 h37.15277 c22.27763,-1.66785 38.9607,-75.75671 26.59321,-76.03825 c-46.781583,2.47691 -49.995146,2.13838 -88.599758,0 c-2.495053,-0.0266 -5.972321,-0.49474 -9.707935,-0.5054 z m2.491319,9.45886 c1.351378,13.69267 3.555849,21.70359 8.018216,33.94345 c-11.382872,-1.50473 -21.069822,-5.22443 -22.851515,-19.10984 c-0.950962,-7.4112 2.390428,-15.16769 14.833299,-14.83361 z " id="path3722" sodipodi:nodetypes="sscccccsccsc" inkscape:connector-curvature="0" style="fill:#000;fill-opacity:1;stroke:#000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"></path></g><g inkscape:groupmode="layer" id="layer2" inkscape:label="Layer 2" style="display:inline"><rect style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.24757317;stroke-opacity:1" id="rect4599" width="34.762054" height="34.762054" x="87.508659" y="18.291576" transform="rotate(25.914715)" ry="5.4825778"></rect><path style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26644793px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 79.804947,57.359056 3.241146,1.609954 V 35.255731 h -3.262698 z" id="path4525" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"></path></g><g inkscape:groupmode="layer" id="layer3" inkscape:label="Layer 3" style="display:inline"><g style="display:inline" id="g4539"><circle style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" id="path4606" cy="90.077766" r="3.4745038" cx="49.064713" transform="rotate(-19.796137)"></circle><circle style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" id="path4606-3" cy="102.1049" r="3.4745038" cx="36.810425" transform="rotate(-19.796137)"></circle><circle style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" id="path4606-1" cy="111.43928" r="3.4745038" cx="46.484283" transform="rotate(-19.796137)"></circle><rect height="27.261492" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.27444693;stroke-opacity:1" x="97.333458" y="18.061695" id="rect4629-8" width="2.6726954" transform="rotate(26.024158)"></rect><path d="M76.558096,68.116343 c12.97589,6.395378 13.012989,4.101862 4.890858,20.907244 " id="path4514" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" style="fill:none;stroke:#000;stroke-width:2.68000007;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"></path></g></g></svg> \ No newline at end of file
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index d4be7289a49..2c185549b24 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -106,7 +106,7 @@
.value.issuable-show-labels.dont-hide.hide-collapsed.qa-labels-block{ class: ("has-labels" if selected_labels.any?) }
- if selected_labels.any?
- selected_labels.each do |label_hash|
- = render_label(label_from_hash(label_hash), link: sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label_hash[:title]))
+ = render_label(label_from_hash(label_hash).present(issuable_subject: nil), link: sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label_hash[:title]))
- else
%span.no-value
= _('None')
diff --git a/app/views/shared/issuable/form/_title.html.haml b/app/views/shared/issuable/form/_title.html.haml
index 56c4b021eab..75e9ab547ce 100644
--- a/app/views/shared/issuable/form/_title.html.haml
+++ b/app/views/shared/issuable/form/_title.html.haml
@@ -6,7 +6,7 @@
%div{ class: div_class }
= form.text_field :title, required: true, maxlength: 255, autofocus: true,
- autocomplete: 'off', class: 'form-control pad qa-issuable-form-title', placeholder: _('Title')
+ autocomplete: 'off', class: 'form-control pad qa-issuable-form-title', placeholder: _('Title'), dir: 'auto'
- if issuable.respond_to?(:work_in_progress?)
.form-text.text-muted
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index 5863f52aa78..ae3ab2adfd0 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -21,7 +21,7 @@
%span.issuable-number= issuable.to_reference
- labels.each do |label|
- = render_label(label, link: polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }))
+ = render_label(label.present(issuable_subject: project), link: polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }))
%span.assignee-icon
- assignees.each do |assignee|
diff --git a/changelogs/unreleased/10327-enable-reliable-fetcher-by-default.yml b/changelogs/unreleased/10327-enable-reliable-fetcher-by-default.yml
new file mode 100644
index 00000000000..89d2fced6e1
--- /dev/null
+++ b/changelogs/unreleased/10327-enable-reliable-fetcher-by-default.yml
@@ -0,0 +1,5 @@
+---
+title: Enable Sidekiq Reliable Fetcher for background jobs by default
+merge_request: 27530
+author:
+type: added
diff --git a/changelogs/unreleased/11254-overflow-ce.yml b/changelogs/unreleased/11254-overflow-ce.yml
new file mode 100644
index 00000000000..dcac46000ac
--- /dev/null
+++ b/changelogs/unreleased/11254-overflow-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Hide ScopedBadge overflow notes
+merge_request: 27651
+author:
+type: fixed
diff --git a/changelogs/unreleased/27424-tklk-gitea-logo.yml b/changelogs/unreleased/27424-tklk-gitea-logo.yml
new file mode 100644
index 00000000000..0d41bb39aad
--- /dev/null
+++ b/changelogs/unreleased/27424-tklk-gitea-logo.yml
@@ -0,0 +1,5 @@
+---
+title: "Use official Gitea logo in importer"
+merge_request: 27424
+author: Matti Ranta (@techknowlogick)
+type: added \ No newline at end of file
diff --git a/changelogs/unreleased/47584-label-text-color.yml b/changelogs/unreleased/47584-label-text-color.yml
new file mode 100644
index 00000000000..7d5eaa62793
--- /dev/null
+++ b/changelogs/unreleased/47584-label-text-color.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve issue where list labels did not have the correct text color on creation
+merge_request: 26794
+author: Tucker Chapman
+type: fixed
diff --git a/changelogs/unreleased/48479-auto-direction-for-issue-title.yml b/changelogs/unreleased/48479-auto-direction-for-issue-title.yml
new file mode 100644
index 00000000000..0571f58ab4d
--- /dev/null
+++ b/changelogs/unreleased/48479-auto-direction-for-issue-title.yml
@@ -0,0 +1,5 @@
+---
+title: Add auto direction for issue title
+merge_request: 27378
+author: Ahmad Haghighi
+type: fixed
diff --git a/changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml b/changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml
new file mode 100644
index 00000000000..8b4f4894048
--- /dev/null
+++ b/changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: Fix 500 in general pipeline settings when passing an invalid build timeout.
+merge_request: 27416
+author:
+type: fixed
diff --git a/changelogs/unreleased/57017-add-toast-success-message.yml b/changelogs/unreleased/57017-add-toast-success-message.yml
new file mode 100644
index 00000000000..931e7755591
--- /dev/null
+++ b/changelogs/unreleased/57017-add-toast-success-message.yml
@@ -0,0 +1,5 @@
+---
+title: Display a toast message when the Kubernetes runner has successfully upgraded.
+merge_request: 27206
+author:
+type: changed
diff --git a/changelogs/unreleased/57293-fix-image-rename.yml b/changelogs/unreleased/57293-fix-image-rename.yml
new file mode 100644
index 00000000000..50dddbdf114
--- /dev/null
+++ b/changelogs/unreleased/57293-fix-image-rename.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Renaming an image via Web IDE corrupts it
+merge_request: 27486
+author:
+type: fixed
diff --git a/changelogs/unreleased/58252-web-ide-dropdown-duplicates.yml b/changelogs/unreleased/58252-web-ide-dropdown-duplicates.yml
new file mode 100644
index 00000000000..48b03994586
--- /dev/null
+++ b/changelogs/unreleased/58252-web-ide-dropdown-duplicates.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Web IDE template dropdown showing duplicates
+merge_request: 27237
+author:
+type: fixed
diff --git a/changelogs/unreleased/58850-fix-misplaced-swipe-view-26969.yml b/changelogs/unreleased/58850-fix-misplaced-swipe-view-26969.yml
new file mode 100644
index 00000000000..fa3e81df4e0
--- /dev/null
+++ b/changelogs/unreleased/58850-fix-misplaced-swipe-view-26969.yml
@@ -0,0 +1,5 @@
+---
+title: "Fix misaligned image diff swipe view"
+merge_request: 26969
+author: ftab
+type: fixed
diff --git a/changelogs/unreleased/59514-uploading-images-base64.yml b/changelogs/unreleased/59514-uploading-images-base64.yml
new file mode 100644
index 00000000000..905b00db06a
--- /dev/null
+++ b/changelogs/unreleased/59514-uploading-images-base64.yml
@@ -0,0 +1,5 @@
+---
+title: Show proper preview for uploaded images in Web IDE
+merge_request: 27471
+author:
+type: fixed
diff --git a/changelogs/unreleased/60387-use-icons-in-user-popovers.yml b/changelogs/unreleased/60387-use-icons-in-user-popovers.yml
new file mode 100644
index 00000000000..100d33690b3
--- /dev/null
+++ b/changelogs/unreleased/60387-use-icons-in-user-popovers.yml
@@ -0,0 +1,5 @@
+---
+title: Show category icons in user popover
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml b/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml
deleted file mode 100644
index df6e6ea4be3..00000000000
--- a/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disable just-in-time Kubernetes resource creation for project level clusters
-merge_request: 27352
-author:
-type: changed
diff --git a/changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml b/changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml
new file mode 100644
index 00000000000..7b5fae016bb
--- /dev/null
+++ b/changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml
@@ -0,0 +1,5 @@
+---
+title: Fix MR popover on ToDos page
+merge_request: 27382
+author:
+type: fixed
diff --git a/changelogs/unreleased/60552-period-dropdown.yml b/changelogs/unreleased/60552-period-dropdown.yml
new file mode 100644
index 00000000000..e1b4a098ab0
--- /dev/null
+++ b/changelogs/unreleased/60552-period-dropdown.yml
@@ -0,0 +1,5 @@
+---
+title: Fix autocomplete dropdown for usernames starting with period
+merge_request: 27533
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/60687-enviro-dropdown.yml b/changelogs/unreleased/60687-enviro-dropdown.yml
new file mode 100644
index 00000000000..1fc5a7dd6f5
--- /dev/null
+++ b/changelogs/unreleased/60687-enviro-dropdown.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Metrics Environments dropdown
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/60906-fix-wiki-links.yml b/changelogs/unreleased/60906-fix-wiki-links.yml
new file mode 100644
index 00000000000..cc65a1382bf
--- /dev/null
+++ b/changelogs/unreleased/60906-fix-wiki-links.yml
@@ -0,0 +1,5 @@
+---
+title: Show proper wiki links in search results
+merge_request: 27634
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-ci-variable-protected-ref.yml b/changelogs/unreleased/add-ci-variable-protected-ref.yml
new file mode 100644
index 00000000000..150ddcc21ad
--- /dev/null
+++ b/changelogs/unreleased/add-ci-variable-protected-ref.yml
@@ -0,0 +1,5 @@
+---
+title: Add CI_COMMIT_REF_PROTECTED CI variable
+merge_request: 26716
+author: Jason van den Hurk
+type: added
diff --git a/changelogs/unreleased/autodevops_remote_private_helm_repository.yml b/changelogs/unreleased/autodevops_remote_private_helm_repository.yml
new file mode 100644
index 00000000000..5341abb1095
--- /dev/null
+++ b/changelogs/unreleased/autodevops_remote_private_helm_repository.yml
@@ -0,0 +1,6 @@
+---
+title: Allow linking to a private helm repository by providing credentials, and customisation
+ of repository name
+merge_request: 27123
+author: Stuart Moore @stjm-cc
+type: added
diff --git a/changelogs/unreleased/ce-remove-already-signed-in.yml b/changelogs/unreleased/ce-remove-already-signed-in.yml
new file mode 100644
index 00000000000..70bed136ced
--- /dev/null
+++ b/changelogs/unreleased/ce-remove-already-signed-in.yml
@@ -0,0 +1,5 @@
+---
+title: Remove "You are already signed in" banner
+merge_request: 27377
+author:
+type: other
diff --git a/changelogs/unreleased/fix-api-ide-relative-url-root.yml b/changelogs/unreleased/fix-api-ide-relative-url-root.yml
new file mode 100644
index 00000000000..8c058645f3e
--- /dev/null
+++ b/changelogs/unreleased/fix-api-ide-relative-url-root.yml
@@ -0,0 +1,5 @@
+---
+title: Fix FE API and IDE handling of '/' relative_url_root
+merge_request: 27635
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-lazy-blobs-requesting-all-previous-blobs.yml b/changelogs/unreleased/fix-lazy-blobs-requesting-all-previous-blobs.yml
new file mode 100644
index 00000000000..58f5a9c943c
--- /dev/null
+++ b/changelogs/unreleased/fix-lazy-blobs-requesting-all-previous-blobs.yml
@@ -0,0 +1,6 @@
+---
+title: Fix Blob.lazy always loading all previously-requested blobs when a new request
+ is made
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/fj-53523-add-option-avoid-loading-wiki-page-content.yml b/changelogs/unreleased/fj-53523-add-option-avoid-loading-wiki-page-content.yml
new file mode 100644
index 00000000000..49eaff52e5a
--- /dev/null
+++ b/changelogs/unreleased/fj-53523-add-option-avoid-loading-wiki-page-content.yml
@@ -0,0 +1,5 @@
+---
+title: Added list_pages method to avoid loading all wiki pages content
+merge_request: 22801
+author:
+type: performance
diff --git a/changelogs/unreleased/fj-60827-fix-web-strategy-error.yml b/changelogs/unreleased/fj-60827-fix-web-strategy-error.yml
new file mode 100644
index 00000000000..ad707ca0225
--- /dev/null
+++ b/changelogs/unreleased/fj-60827-fix-web-strategy-error.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug when project export to remote url fails
+merge_request: 27614
+author:
+type: fixed
diff --git a/changelogs/unreleased/jc-update-list-last-commits.yml b/changelogs/unreleased/jc-update-list-last-commits.yml
new file mode 100644
index 00000000000..0e72c4255ae
--- /dev/null
+++ b/changelogs/unreleased/jc-update-list-last-commits.yml
@@ -0,0 +1,5 @@
+---
+title: Client side changes for ListLastCommitsForTree response update
+merge_request: 26880
+author:
+type: fixed
diff --git a/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml b/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml
deleted file mode 100644
index c1669a484aa..00000000000
--- a/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade Gitaly to 1.34.0
-merge_request: 27494
-author:
-type: fixed
diff --git a/changelogs/unreleased/rails5-1.yml b/changelogs/unreleased/rails5-1.yml
new file mode 100644
index 00000000000..da16735bb0d
--- /dev/null
+++ b/changelogs/unreleased/rails5-1.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade to Rails 5.1
+merge_request: 27480
+author: Jasper Maes
+type: other
diff --git a/changelogs/unreleased/refactor-58829-migrate-notes-spec-to-jest.yml b/changelogs/unreleased/refactor-58829-migrate-notes-spec-to-jest.yml
new file mode 100644
index 00000000000..9a1886797da
--- /dev/null
+++ b/changelogs/unreleased/refactor-58829-migrate-notes-spec-to-jest.yml
@@ -0,0 +1,5 @@
+---
+title: 'Refactored notes tests from Karma to Jest'
+merge_request: 27648
+author: Martin Hobert
+type: other
diff --git a/changelogs/unreleased/sh-avoid-fetching-temp-refs-within-project.yml b/changelogs/unreleased/sh-avoid-fetching-temp-refs-within-project.yml
new file mode 100644
index 00000000000..7511543f7f6
--- /dev/null
+++ b/changelogs/unreleased/sh-avoid-fetching-temp-refs-within-project.yml
@@ -0,0 +1,5 @@
+---
+title: Don't create a temp reference for branch comparisons within project
+merge_request: 24038
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml b/changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml
deleted file mode 100644
index eb8774d652f..00000000000
--- a/changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bring back Rugged implementation of ListCommitsByOid
-merge_request: 27441
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-bump-ruby-required-version-check.yml b/changelogs/unreleased/sh-bump-ruby-required-version-check.yml
deleted file mode 100644
index b5b6eb87650..00000000000
--- a/changelogs/unreleased/sh-bump-ruby-required-version-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump required Ruby version check to 2.5.3
-merge_request: 27495
-author:
-type: other
diff --git a/changelogs/unreleased/sh-disable-internal-ids-available-check.yml b/changelogs/unreleased/sh-disable-internal-ids-available-check.yml
new file mode 100644
index 00000000000..069a9ba7d69
--- /dev/null
+++ b/changelogs/unreleased/sh-disable-internal-ids-available-check.yml
@@ -0,0 +1,5 @@
+---
+title: Always use internal ID tables in development and production
+merge_request: 27544
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-autocomplete-mirror-repo.yml b/changelogs/unreleased/sh-fix-autocomplete-mirror-repo.yml
new file mode 100644
index 00000000000..e855684bab1
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-autocomplete-mirror-repo.yml
@@ -0,0 +1,5 @@
+---
+title: Disable password autocomplete in mirror repository form
+merge_request: 27542
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-upgrade-grpc-and-protobuf.yml b/changelogs/unreleased/sh-upgrade-grpc-and-protobuf.yml
new file mode 100644
index 00000000000..a43a59a4f8a
--- /dev/null
+++ b/changelogs/unreleased/sh-upgrade-grpc-and-protobuf.yml
@@ -0,0 +1,5 @@
+---
+title: Bump gRPC to 1.19.0 and protobuf to 3.7.1
+merge_request: 27086
+author:
+type: other
diff --git a/changelogs/unreleased/support-negative-matches.yml b/changelogs/unreleased/support-negative-matches.yml
new file mode 100644
index 00000000000..8d3f2d3cbae
--- /dev/null
+++ b/changelogs/unreleased/support-negative-matches.yml
@@ -0,0 +1,5 @@
+---
+title: Support negative matches
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/update-workhorse-master.yml b/changelogs/unreleased/update-workhorse-master.yml
new file mode 100644
index 00000000000..97e2e891ab1
--- /dev/null
+++ b/changelogs/unreleased/update-workhorse-master.yml
@@ -0,0 +1,5 @@
+---
+title: Update Workhorse to v8.7.0
+merge_request: 27630
+author:
+type: fixed
diff --git a/config/application.rb b/config/application.rb
index cbcfef34e01..cddd91f267a 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -164,9 +164,6 @@ module Gitlab
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
- # Can be removed once upgraded to Rails 5.1 or higher
- config.action_controller.raise_on_unfiltered_parameters = true
-
# Nokogiri is significantly faster and uses less memory than REXML
ActiveSupport::XmlMini.backend = 'Nokogiri'
diff --git a/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb b/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb
deleted file mode 100644
index 228ced32188..00000000000
--- a/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-# This is a monkey patch which must be removed when migrating to Rails 5.1 from 5.0.
-#
-# In Rails 5.0 there was introduced a bug which casts types in the uniqueness validator.
-# https://github.com/rails/rails/pull/23523/commits/811a4fa8eb6ceea841e61e8ac05747ffb69595ae
-#
-# That causes to bugs like this:
-#
-# 1) API::Users POST /user/:id/gpg_keys/:key_id/revoke when authenticated revokes existing key
-# Failure/Error: let(:gpg_key) { create(:gpg_key, user: user) }
-#
-# TypeError:
-# can't cast Hash
-# # ./spec/requests/api/users_spec.rb:7:in `block (2 levels) in <top (required)>'
-# # ./spec/requests/api/users_spec.rb:908:in `block (4 levels) in <top (required)>'
-# # ------------------
-# # --- Caused by: ---
-# # TypeError:
-# # TypeError
-# # ./spec/requests/api/users_spec.rb:7:in `block (2 levels) in <top (required)>'
-#
-# This bug was fixed in Rails 5.1 by https://github.com/rails/rails/pull/24745/commits/aa062318c451512035c10898a1af95943b1a3803
-
-if Rails.gem_version >= Gem::Version.new("5.1")
- raise "Remove this monkey patch: #{__FILE__}"
-end
-
-# Copy-paste from https://github.com/kamipo/rails/blob/aa062318c451512035c10898a1af95943b1a3803/activerecord/lib/active_record/validations/uniqueness.rb
-# including local fixes to make Rubocop happy again.
-module ActiveRecord
- module Validations
- class UniquenessValidator < ActiveModel::EachValidator # :nodoc:
- def validate_each(record, attribute, value)
- finder_class = find_finder_class_for(record)
- table = finder_class.arel_table
- value = map_enum_attribute(finder_class, attribute, value)
-
- relation = build_relation(finder_class, table, attribute, value)
-
- if record.persisted?
- if finder_class.primary_key
- relation = relation.where.not(finder_class.primary_key => record.id_was || record.id)
- else
- raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
- end
- end
-
- relation = scope_relation(record, table, relation)
- relation = relation.merge(options[:conditions]) if options[:conditions]
-
- if relation.exists?
- error_options = options.except(:case_sensitive, :scope, :conditions)
- error_options[:value] = value
-
- record.errors.add(attribute, :taken, error_options)
- end
- rescue RangeError
- end
-
- protected
-
- def build_relation(klass, table, attribute, value) #:nodoc:
- if reflection = klass._reflect_on_association(attribute)
- attribute = reflection.foreign_key
- value = value.attributes[reflection.klass.primary_key] unless value.nil?
- end
-
- # the attribute may be an aliased attribute
- if klass.attribute_alias?(attribute)
- attribute = klass.attribute_alias(attribute)
- end
-
- attribute_name = attribute.to_s
-
- column = klass.columns_hash[attribute_name]
- cast_type = klass.type_for_attribute(attribute_name)
-
- comparison =
- if !options[:case_sensitive] && !value.nil?
- # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
- klass.connection.case_insensitive_comparison(table, attribute, column, value)
- else
- klass.connection.case_sensitive_comparison(table, attribute, column, value)
- end
-
- if value.nil?
- klass.unscoped.where(comparison)
- else
- bind = Relation::QueryAttribute.new(attribute_name, value, cast_type)
- klass.unscoped.where(comparison, bind)
- end
- end
- end
- end
-end
diff --git a/config/initializers/active_record_build_select.rb b/config/initializers/active_record_build_select.rb
new file mode 100644
index 00000000000..ab5a872cac6
--- /dev/null
+++ b/config/initializers/active_record_build_select.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+# rubocop:disable Gitlab/ModuleWithInstanceVariables
+
+# build_select only selects the required fields if the model has ignored_columns.
+# This is incompatible with some migrations or background migration specs because
+# rails keeps a statement cache in memory. So if a model with ignored_columns in a
+# migration is used, the query with select table.col1, table.col2 is stored in the
+# statement cache. If a different migration is then run and one of these columns is
+# removed in the meantime, the query is invalid.
+
+module ActiveRecord
+ module QueryMethods
+ private
+
+ def build_select(arel)
+ if select_values.any?
+ arel.project(*arel_columns(select_values.uniq))
+ else
+ arel.project(@klass.arel_table[Arel.star])
+ end
+ end
+ end
+end
diff --git a/config/initializers/active_record_locking.rb b/config/initializers/active_record_locking.rb
deleted file mode 100644
index 1bd1a12e4b7..00000000000
--- a/config/initializers/active_record_locking.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-# rubocop:disable Lint/RescueException
-
-# Remove this monkey patch when we move to Rails 5.1, because the bug has been fixed in https://github.com/rails/rails/pull/26050.
-if Rails.gem_version >= Gem::Version.new("5.1")
- raise "Remove this monkey patch: #{__FILE__}"
-end
-
-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?
-
- lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i
- increment_lock
-
- attribute_names += [lock_col]
- attribute_names.uniq!
-
- begin
- relation = self.class.unscoped
-
- affected_rows = relation.where(
- self.class.primary_key => id,
- # Patched because when `lock_version` is read as `0`, it may actually be `NULL` in the DB.
- lock_col => previous_lock_value == 0 ? [nil, 0] : 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
-
- affected_rows
-
- # If something went wrong, revert the version.
- rescue Exception
- send(lock_col + '=', previous_lock_value)
- 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?
-
- 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`
- class LockingType
- def deserialize(value)
- super
- end
-
- def serialize(value)
- super
- end
- end
- end
-end
diff --git a/config/initializers/ar_native_database_types.rb b/config/initializers/ar_native_database_types.rb
index 3522b1db536..6d397661f75 100644
--- a/config/initializers/ar_native_database_types.rb
+++ b/config/initializers/ar_native_database_types.rb
@@ -4,7 +4,8 @@ module ActiveRecord
module ConnectionAdapters
class AbstractMysqlAdapter
NATIVE_DATABASE_TYPES.merge!(
- bigserial: { name: 'bigint(20) auto_increment PRIMARY KEY' }
+ bigserial: { name: 'bigint(20) auto_increment PRIMARY KEY' },
+ serial: { name: 'int auto_increment PRIMARY KEY' }
)
end
end
diff --git a/config/initializers/postgresql_opclasses_support.rb b/config/initializers/postgresql_opclasses_support.rb
index b066f3788ec..7e912180820 100644
--- a/config/initializers/postgresql_opclasses_support.rb
+++ b/config/initializers/postgresql_opclasses_support.rb
@@ -78,7 +78,7 @@ module ActiveRecord
if index_name.length > max_index_length
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
end
- if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false)
+ if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
end
index_columns = quoted_columns_for_index(column_names, options).join(", ")
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 2e4aa9c1053..7b69cf11288 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -1,5 +1,17 @@
require 'sidekiq/web'
+def enable_reliable_fetch?
+ return true unless Feature::FlipperFeature.table_exists?
+
+ Feature.enabled?(:gitlab_sidekiq_reliable_fetcher, default_enabled: true)
+end
+
+def enable_semi_reliable_fetch_mode?
+ return true unless Feature::FlipperFeature.table_exists?
+
+ Feature.enabled?(:gitlab_sidekiq_enable_semi_reliable_fetcher, default_enabled: true)
+end
+
# Disable the Sidekiq Rack session since GitLab already has its own session store.
# CSRF protection still works (https://github.com/mperham/sidekiq/commit/315504e766c4fd88a29b7772169060afc4c40329).
Sidekiq::Web.set :sessions, false
@@ -45,9 +57,8 @@ Sidekiq.configure_server do |config|
ActiveRecord::Base.clear_all_connections!
end
- if Feature::FlipperFeature.table_exists? && Feature.enabled?(:gitlab_sidekiq_reliable_fetcher)
- # By default we're going to use Semi Reliable Fetch
- config.options[:semi_reliable_fetch] = Feature.enabled?(:gitlab_sidekiq_enable_semi_reliable_fetcher, default_enabled: true)
+ if enable_reliable_fetch?
+ config.options[:semi_reliable_fetch] = enable_semi_reliable_fetch_mode?
Sidekiq::ReliableFetch.setup_reliable_fetch!(config)
end
diff --git a/danger/ce_ee_vue_templates/Dangerfile b/danger/ce_ee_vue_templates/Dangerfile
new file mode 100644
index 00000000000..f7715eb2a89
--- /dev/null
+++ b/danger/ce_ee_vue_templates/Dangerfile
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+require 'cgi'
+
+def get_vue_files_with_ce_and_ee_versions(files)
+ files.select do |file|
+ if file.end_with?('.vue')
+ counterpart_path = if file.start_with?('ee/')
+ file.delete_prefix('ee/')
+ else
+ "ee/#{file}"
+ end
+
+ escaped_path = CGI.escape(counterpart_path)
+ api_endpoint = "https://gitlab.com/api/v4/projects/gitlab-org%2Fgitlab-ee/repository/files/#{escaped_path}?ref=master"
+ response = HTTParty.get(api_endpoint) # rubocop:disable Gitlab/HTTParty
+ response.code != 404
+ else
+ false
+ end
+ end
+end
+
+vue_candidates = get_vue_files_with_ce_and_ee_versions(helper.all_changed_files)
+
+return if vue_candidates.empty?
+
+message 'This merge request includes changes to Vue files that have both CE and EE versions.'
+
+markdown(<<~MARKDOWN)
+ ## Vue `<template>` in CE and EE
+
+ Some Vue files in CE have a counterpart in EE.
+ (For example, `path/to/file.vue` and `ee/path/to/file.vue`.)
+
+ When run in the context of CE, the `<template>` of the CE Vue file is used.
+ When run in the context of EE, the `<template>` of the EE Vue file is used.
+
+ It's easy to accidentally make a change to a CE `<template>` that _should_
+ appear in both CE and EE without making the change in both places.
+ When this happens, the change only takes effect in CE.
+
+ The following Vue files were changed as part of this merge request that
+ include both a CE and EE version of the file:
+
+ * #{vue_candidates.map { |path| "`#{path}`" }.join("\n* ")}
+
+ If you made a change to the `<template>` of any of these Vue files that
+ should be visible in both CE and EE, please ensure you have made your
+ change to both versions of the file.
+
+ ### A better alternative
+
+ An even _better_ alternative is to refactor this component to only use
+ a single template for both CE and EE. More info on this approach here:
+ https://docs.gitlab.com/ee/development/ee_features.html#template-tag
+MARKDOWN
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index 926401d8b9e..582a5203d1d 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -1,23 +1,5 @@
require './spec/support/sidekiq'
Gitlab::Seeder.quiet do
- Project.all.each do |project|
- 10.times do
- label_ids = project.labels.pluck(:id).sample(3)
- label_ids += project.group.labels.sample(3) if project.group
-
- issue_params = {
- title: FFaker::Lorem.sentence(6),
- description: FFaker::Lorem.sentence,
- state: ['opened', 'closed'].sample,
- milestone: project.milestones.sample,
- assignees: [project.team.users.sample],
- created_at: rand(12).months.ago,
- label_ids: label_ids
- }
-
- Issues::CreateService.new(project, project.team.users.sample, issue_params).execute
- print '.'
- end
- end
+ Rake::Task["gitlab:seed:issues"].invoke
end
diff --git a/db/migrate/20161031181638_add_unique_index_to_subscriptions.rb b/db/migrate/20161031181638_add_unique_index_to_subscriptions.rb
index 23a775d6282..9005b42b41f 100644
--- a/db/migrate/20161031181638_add_unique_index_to_subscriptions.rb
+++ b/db/migrate/20161031181638_add_unique_index_to_subscriptions.rb
@@ -9,11 +9,11 @@ class AddUniqueIndexToSubscriptions < ActiveRecord::Migration[4.2]
def up
add_concurrent_index :subscriptions, [:subscribable_id, :subscribable_type, :user_id, :project_id], { unique: true, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' }
- remove_index :subscriptions, name: 'subscriptions_user_id_and_ref_fields' if index_name_exists?(:subscriptions, 'subscriptions_user_id_and_ref_fields', false)
+ remove_index :subscriptions, name: 'subscriptions_user_id_and_ref_fields' if index_name_exists?(:subscriptions, 'subscriptions_user_id_and_ref_fields')
end
def down
add_concurrent_index :subscriptions, [:subscribable_id, :subscribable_type, :user_id], { unique: true, name: 'subscriptions_user_id_and_ref_fields' }
- remove_index :subscriptions, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' if index_name_exists?(:subscriptions, 'index_subscriptions_on_subscribable_and_user_id_and_project_id', false)
+ remove_index :subscriptions, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' if index_name_exists?(:subscriptions, 'index_subscriptions_on_subscribable_and_user_id_and_project_id')
end
end
diff --git a/db/migrate/20190322164830_add_auto_ssl_enabled_to_pages_domain.rb b/db/migrate/20190322164830_add_auto_ssl_enabled_to_pages_domain.rb
new file mode 100644
index 00000000000..e74a9535ddf
--- /dev/null
+++ b/db/migrate/20190322164830_add_auto_ssl_enabled_to_pages_domain.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddAutoSslEnabledToPagesDomain < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :pages_domains, :auto_ssl_enabled, :boolean, default: false
+ end
+
+ def down
+ remove_column :pages_domains, :auto_ssl_enabled
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c0399529deb..3a5d567ac57 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -16,7 +16,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
enable_extension "plpgsql"
enable_extension "pg_trgm"
- create_table "abuse_reports", force: :cascade do |t|
+ create_table "abuse_reports", id: :serial, force: :cascade do |t|
t.integer "reporter_id"
t.integer "user_id"
t.text "message"
@@ -26,7 +26,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.integer "cached_markdown_version"
end
- create_table "appearances", force: :cascade do |t|
+ create_table "appearances", id: :serial, force: :cascade do |t|
t.string "title", null: false
t.text "description", null: false
t.string "header_logo"
@@ -47,13 +47,13 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.boolean "email_header_and_footer_enabled", default: false, null: false
end
- create_table "application_setting_terms", force: :cascade do |t|
+ create_table "application_setting_terms", id: :serial, force: :cascade do |t|
t.integer "cached_markdown_version"
t.text "terms", null: false
t.text "terms_html"
end
- create_table "application_settings", force: :cascade do |t|
+ create_table "application_settings", id: :serial, force: :cascade do |t|
t.integer "default_projects_limit"
t.boolean "signup_enabled"
t.boolean "gravatar_enabled"
@@ -190,7 +190,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree
end
- create_table "audit_events", force: :cascade do |t|
+ create_table "audit_events", id: :serial, force: :cascade do |t|
t.integer "author_id", null: false
t.string "type", null: false
t.integer "entity_id", null: false
@@ -201,7 +201,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree
end
- create_table "award_emoji", force: :cascade do |t|
+ create_table "award_emoji", id: :serial, force: :cascade do |t|
t.string "name"
t.integer "user_id"
t.integer "awardable_id"
@@ -212,7 +212,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id", "name"], name: "index_award_emoji_on_user_id_and_name", using: :btree
end
- create_table "badges", force: :cascade do |t|
+ create_table "badges", id: :serial, force: :cascade do |t|
t.string "link_url", null: false
t.string "image_url", null: false
t.integer "project_id"
@@ -224,7 +224,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_badges_on_project_id", using: :btree
end
- create_table "board_group_recent_visits", id: :bigserial, force: :cascade do |t|
+ create_table "board_group_recent_visits", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "user_id"
@@ -236,7 +236,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_board_group_recent_visits_on_user_id", using: :btree
end
- create_table "board_project_recent_visits", id: :bigserial, force: :cascade do |t|
+ create_table "board_project_recent_visits", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "user_id"
@@ -248,7 +248,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_board_project_recent_visits_on_user_id", using: :btree
end
- create_table "boards", force: :cascade do |t|
+ create_table "boards", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@@ -257,7 +257,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_boards_on_project_id", using: :btree
end
- create_table "broadcast_messages", force: :cascade do |t|
+ create_table "broadcast_messages", id: :serial, force: :cascade do |t|
t.text "message", null: false
t.datetime "starts_at", null: false
t.datetime "ends_at", null: false
@@ -270,7 +270,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["starts_at", "ends_at", "id"], name: "index_broadcast_messages_on_starts_at_and_ends_at_and_id", using: :btree
end
- create_table "chat_names", force: :cascade do |t|
+ create_table "chat_names", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "service_id", null: false
t.string "team_id", null: false
@@ -284,7 +284,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id", "service_id"], name: "index_chat_names_on_user_id_and_service_id", unique: true, using: :btree
end
- create_table "chat_teams", force: :cascade do |t|
+ create_table "chat_teams", id: :serial, force: :cascade do |t|
t.integer "namespace_id", null: false
t.string "team_id"
t.string "name"
@@ -293,7 +293,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["namespace_id"], name: "index_chat_teams_on_namespace_id", unique: true, using: :btree
end
- create_table "ci_build_trace_chunks", id: :bigserial, force: :cascade do |t|
+ create_table "ci_build_trace_chunks", force: :cascade do |t|
t.integer "build_id", null: false
t.integer "chunk_index", null: false
t.integer "data_store", null: false
@@ -301,13 +301,13 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["build_id", "chunk_index"], name: "index_ci_build_trace_chunks_on_build_id_and_chunk_index", unique: true, using: :btree
end
- create_table "ci_build_trace_section_names", force: :cascade do |t|
+ create_table "ci_build_trace_section_names", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.index ["project_id", "name"], name: "index_ci_build_trace_section_names_on_project_id_and_name", unique: true, using: :btree
end
- create_table "ci_build_trace_sections", force: :cascade do |t|
+ create_table "ci_build_trace_sections", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.datetime_with_timezone "date_start", null: false
t.datetime_with_timezone "date_end", null: false
@@ -320,7 +320,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["section_name_id"], name: "index_ci_build_trace_sections_on_section_name_id", using: :btree
end
- create_table "ci_builds", force: :cascade do |t|
+ create_table "ci_builds", id: :serial, force: :cascade do |t|
t.string "status"
t.datetime "finished_at"
t.text "trace"
@@ -387,7 +387,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_ci_builds_on_user_id", using: :btree
end
- create_table "ci_builds_metadata", force: :cascade do |t|
+ create_table "ci_builds_metadata", id: :serial, force: :cascade do |t|
t.integer "build_id", null: false
t.integer "project_id", null: false
t.integer "timeout"
@@ -398,7 +398,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_ci_builds_metadata_on_project_id", using: :btree
end
- create_table "ci_builds_runner_session", id: :bigserial, force: :cascade do |t|
+ create_table "ci_builds_runner_session", force: :cascade do |t|
t.integer "build_id", null: false
t.string "url", null: false
t.string "certificate"
@@ -406,7 +406,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["build_id"], name: "index_ci_builds_runner_session_on_build_id", unique: true, using: :btree
end
- create_table "ci_group_variables", force: :cascade do |t|
+ create_table "ci_group_variables", id: :serial, force: :cascade do |t|
t.string "key", null: false
t.text "value"
t.text "encrypted_value"
@@ -420,7 +420,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree
end
- create_table "ci_job_artifacts", force: :cascade do |t|
+ create_table "ci_job_artifacts", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "job_id", null: false
t.integer "file_type", null: false
@@ -439,7 +439,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_ci_job_artifacts_on_project_id", using: :btree
end
- create_table "ci_pipeline_chat_data", id: :bigserial, force: :cascade do |t|
+ create_table "ci_pipeline_chat_data", force: :cascade do |t|
t.integer "pipeline_id", null: false
t.integer "chat_name_id", null: false
t.text "response_url", null: false
@@ -459,7 +459,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree
end
- create_table "ci_pipeline_schedules", force: :cascade do |t|
+ create_table "ci_pipeline_schedules", id: :serial, force: :cascade do |t|
t.string "description"
t.string "ref"
t.string "cron"
@@ -475,7 +475,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_ci_pipeline_schedules_on_project_id", using: :btree
end
- create_table "ci_pipeline_variables", force: :cascade do |t|
+ create_table "ci_pipeline_variables", id: :serial, force: :cascade do |t|
t.string "key", null: false
t.text "value"
t.text "encrypted_value"
@@ -485,7 +485,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["pipeline_id", "key"], name: "index_ci_pipeline_variables_on_pipeline_id_and_key", unique: true, using: :btree
end
- create_table "ci_pipelines", force: :cascade do |t|
+ create_table "ci_pipelines", id: :serial, force: :cascade do |t|
t.string "ref"
t.string "sha"
t.string "before_sha"
@@ -525,14 +525,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_ci_pipelines_on_user_id", using: :btree
end
- create_table "ci_runner_namespaces", force: :cascade do |t|
+ create_table "ci_runner_namespaces", id: :serial, force: :cascade do |t|
t.integer "runner_id"
t.integer "namespace_id"
t.index ["namespace_id"], name: "index_ci_runner_namespaces_on_namespace_id", using: :btree
t.index ["runner_id", "namespace_id"], name: "index_ci_runner_namespaces_on_runner_id_and_namespace_id", unique: true, using: :btree
end
- create_table "ci_runner_projects", force: :cascade do |t|
+ create_table "ci_runner_projects", id: :serial, force: :cascade do |t|
t.integer "runner_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
@@ -541,7 +541,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree
end
- create_table "ci_runners", force: :cascade do |t|
+ create_table "ci_runners", id: :serial, force: :cascade do |t|
t.string "token"
t.datetime "created_at"
t.datetime "updated_at"
@@ -569,7 +569,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["token_encrypted"], name: "index_ci_runners_on_token_encrypted", using: :btree
end
- create_table "ci_stages", force: :cascade do |t|
+ create_table "ci_stages", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.integer "pipeline_id"
t.datetime "created_at"
@@ -584,7 +584,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_ci_stages_on_project_id", using: :btree
end
- create_table "ci_trigger_requests", force: :cascade do |t|
+ create_table "ci_trigger_requests", id: :serial, force: :cascade do |t|
t.integer "trigger_id", null: false
t.text "variables"
t.datetime "created_at"
@@ -594,7 +594,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["trigger_id"], name: "index_ci_trigger_requests_on_trigger_id", using: :btree
end
- create_table "ci_triggers", force: :cascade do |t|
+ create_table "ci_triggers", id: :serial, force: :cascade do |t|
t.string "token"
t.datetime "created_at"
t.datetime "updated_at"
@@ -606,7 +606,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_ci_triggers_on_project_id", using: :btree
end
- create_table "ci_variables", force: :cascade do |t|
+ create_table "ci_variables", id: :serial, force: :cascade do |t|
t.string "key", null: false
t.text "value"
t.text "encrypted_value"
@@ -619,14 +619,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree
end
- create_table "cluster_groups", force: :cascade do |t|
+ create_table "cluster_groups", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "group_id", null: false
t.index ["cluster_id", "group_id"], name: "index_cluster_groups_on_cluster_id_and_group_id", unique: true, using: :btree
t.index ["group_id"], name: "index_cluster_groups_on_group_id", using: :btree
end
- create_table "cluster_platforms_kubernetes", force: :cascade do |t|
+ create_table "cluster_platforms_kubernetes", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
@@ -642,7 +642,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["cluster_id"], name: "index_cluster_platforms_kubernetes_on_cluster_id", unique: true, using: :btree
end
- create_table "cluster_projects", force: :cascade do |t|
+ create_table "cluster_projects", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
@@ -651,7 +651,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_cluster_projects_on_project_id", using: :btree
end
- create_table "cluster_providers_gcp", force: :cascade do |t|
+ create_table "cluster_providers_gcp", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "status"
t.integer "num_nodes", null: false
@@ -669,7 +669,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["cluster_id"], name: "index_cluster_providers_gcp_on_cluster_id", unique: true, using: :btree
end
- create_table "clusters", force: :cascade do |t|
+ create_table "clusters", id: :serial, force: :cascade do |t|
t.integer "user_id"
t.integer "provider_type"
t.integer "platform_type"
@@ -685,7 +685,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_clusters_on_user_id", using: :btree
end
- create_table "clusters_applications_cert_managers", force: :cascade do |t|
+ create_table "clusters_applications_cert_managers", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "status", null: false
t.string "version", null: false
@@ -696,7 +696,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["cluster_id"], name: "index_clusters_applications_cert_managers_on_cluster_id", unique: true, using: :btree
end
- create_table "clusters_applications_helm", force: :cascade do |t|
+ create_table "clusters_applications_helm", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
@@ -709,7 +709,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["cluster_id"], name: "index_clusters_applications_helm_on_cluster_id", unique: true, using: :btree
end
- create_table "clusters_applications_ingress", force: :cascade do |t|
+ create_table "clusters_applications_ingress", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
@@ -723,7 +723,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["cluster_id"], name: "index_clusters_applications_ingress_on_cluster_id", unique: true, using: :btree
end
- create_table "clusters_applications_jupyter", force: :cascade do |t|
+ create_table "clusters_applications_jupyter", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "oauth_application_id"
t.integer "status", null: false
@@ -736,7 +736,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["oauth_application_id"], name: "index_clusters_applications_jupyter_on_oauth_application_id", using: :btree
end
- create_table "clusters_applications_knative", force: :cascade do |t|
+ create_table "clusters_applications_knative", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
@@ -749,7 +749,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["cluster_id"], name: "index_clusters_applications_knative_on_cluster_id", unique: true, using: :btree
end
- create_table "clusters_applications_prometheus", force: :cascade do |t|
+ create_table "clusters_applications_prometheus", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "status", null: false
t.string "version", null: false
@@ -759,7 +759,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["cluster_id"], name: "index_clusters_applications_prometheus_on_cluster_id", unique: true, using: :btree
end
- create_table "clusters_applications_runners", force: :cascade do |t|
+ create_table "clusters_applications_runners", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "runner_id"
t.integer "status", null: false
@@ -772,7 +772,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["runner_id"], name: "index_clusters_applications_runners_on_runner_id", using: :btree
end
- create_table "clusters_kubernetes_namespaces", id: :bigserial, force: :cascade do |t|
+ create_table "clusters_kubernetes_namespaces", force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "project_id"
t.integer "cluster_project_id"
@@ -788,7 +788,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_clusters_kubernetes_namespaces_on_project_id", using: :btree
end
- create_table "container_repositories", force: :cascade do |t|
+ create_table "container_repositories", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at", null: false
@@ -797,7 +797,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_container_repositories_on_project_id", using: :btree
end
- create_table "conversational_development_index_metrics", force: :cascade do |t|
+ create_table "conversational_development_index_metrics", id: :serial, force: :cascade do |t|
t.float "leader_issues", null: false
t.float "instance_issues", null: false
t.float "leader_notes", null: false
@@ -832,7 +832,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.float "percentage_service_desk_issues", default: 0.0, null: false
end
- create_table "deploy_keys_projects", force: :cascade do |t|
+ create_table "deploy_keys_projects", id: :serial, force: :cascade do |t|
t.integer "deploy_key_id", null: false
t.integer "project_id", null: false
t.datetime "created_at"
@@ -841,7 +841,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
end
- create_table "deploy_tokens", force: :cascade do |t|
+ create_table "deploy_tokens", id: :serial, force: :cascade do |t|
t.boolean "revoked", default: false
t.boolean "read_repository", default: false, null: false
t.boolean "read_registry", default: false, null: false
@@ -853,7 +853,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["token"], name: "index_deploy_tokens_on_token", unique: true, using: :btree
end
- create_table "deployments", force: :cascade do |t|
+ create_table "deployments", id: :serial, force: :cascade do |t|
t.integer "iid", null: false
t.integer "project_id", null: false
t.integer "environment_id", null: false
@@ -879,7 +879,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id", "status"], name: "index_deployments_on_project_id_and_status", using: :btree
end
- create_table "emails", force: :cascade do |t|
+ create_table "emails", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.string "email", null: false
t.datetime "created_at"
@@ -892,7 +892,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_emails_on_user_id", using: :btree
end
- create_table "environments", force: :cascade do |t|
+ create_table "environments", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at"
@@ -905,7 +905,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id", "slug"], name: "index_environments_on_project_id_and_slug", unique: true, using: :btree
end
- create_table "events", force: :cascade do |t|
+ create_table "events", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.integer "author_id", null: false
t.integer "target_id"
@@ -920,7 +920,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["target_type", "target_id"], name: "index_events_on_target_type_and_target_id", using: :btree
end
- create_table "feature_gates", force: :cascade do |t|
+ create_table "feature_gates", id: :serial, force: :cascade do |t|
t.string "feature_key", null: false
t.string "key", null: false
t.string "value"
@@ -929,14 +929,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["feature_key", "key", "value"], name: "index_feature_gates_on_feature_key_and_key_and_value", unique: true, using: :btree
end
- create_table "features", force: :cascade do |t|
+ create_table "features", id: :serial, force: :cascade do |t|
t.string "key", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["key"], name: "index_features_on_key", unique: true, using: :btree
end
- create_table "fork_network_members", force: :cascade do |t|
+ create_table "fork_network_members", id: :serial, force: :cascade do |t|
t.integer "fork_network_id", null: false
t.integer "project_id", null: false
t.integer "forked_from_project_id"
@@ -945,13 +945,13 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_fork_network_members_on_project_id", unique: true, using: :btree
end
- create_table "fork_networks", force: :cascade do |t|
+ create_table "fork_networks", id: :serial, force: :cascade do |t|
t.integer "root_project_id"
t.string "deleted_root_project_name"
t.index ["root_project_id"], name: "index_fork_networks_on_root_project_id", unique: true, using: :btree
end
- create_table "forked_project_links", force: :cascade do |t|
+ create_table "forked_project_links", id: :serial, force: :cascade do |t|
t.integer "forked_to_project_id", null: false
t.integer "forked_from_project_id", null: false
t.datetime "created_at"
@@ -959,7 +959,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree
end
- create_table "gpg_key_subkeys", force: :cascade do |t|
+ create_table "gpg_key_subkeys", id: :serial, force: :cascade do |t|
t.integer "gpg_key_id", null: false
t.binary "keyid"
t.binary "fingerprint"
@@ -968,7 +968,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["keyid"], name: "index_gpg_key_subkeys_on_keyid", unique: true, using: :btree
end
- create_table "gpg_keys", force: :cascade do |t|
+ create_table "gpg_keys", id: :serial, force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "user_id"
@@ -980,7 +980,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_gpg_keys_on_user_id", using: :btree
end
- create_table "gpg_signatures", force: :cascade do |t|
+ create_table "gpg_signatures", id: :serial, force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "project_id"
@@ -998,7 +998,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_gpg_signatures_on_project_id", using: :btree
end
- create_table "group_custom_attributes", force: :cascade do |t|
+ create_table "group_custom_attributes", id: :serial, force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "group_id", null: false
@@ -1008,7 +1008,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["key", "value"], name: "index_group_custom_attributes_on_key_and_value", using: :btree
end
- create_table "identities", force: :cascade do |t|
+ create_table "identities", id: :serial, force: :cascade do |t|
t.string "extern_uid"
t.string "provider"
t.integer "user_id"
@@ -1017,7 +1017,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_identities_on_user_id", using: :btree
end
- create_table "import_export_uploads", force: :cascade do |t|
+ create_table "import_export_uploads", id: :serial, force: :cascade do |t|
t.datetime_with_timezone "updated_at", null: false
t.integer "project_id"
t.text "import_file"
@@ -1026,7 +1026,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["updated_at"], name: "index_import_export_uploads_on_updated_at", using: :btree
end
- create_table "internal_ids", id: :bigserial, force: :cascade do |t|
+ create_table "internal_ids", force: :cascade do |t|
t.integer "project_id"
t.integer "usage", null: false
t.integer "last_value", null: false
@@ -1044,7 +1044,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_issue_assignees_on_user_id", using: :btree
end
- create_table "issue_metrics", force: :cascade do |t|
+ create_table "issue_metrics", id: :serial, force: :cascade do |t|
t.integer "issue_id", null: false
t.datetime "first_mentioned_in_commit_at"
t.datetime "first_associated_with_milestone_at"
@@ -1054,7 +1054,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["issue_id"], name: "index_issue_metrics", using: :btree
end
- create_table "issues", force: :cascade do |t|
+ create_table "issues", id: :serial, force: :cascade do |t|
t.string "title"
t.integer "author_id"
t.integer "project_id"
@@ -1097,7 +1097,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["updated_by_id"], name: "index_issues_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree
end
- create_table "keys", force: :cascade do |t|
+ create_table "keys", id: :serial, force: :cascade do |t|
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
@@ -1111,7 +1111,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_keys_on_user_id", using: :btree
end
- create_table "label_links", force: :cascade do |t|
+ create_table "label_links", id: :serial, force: :cascade do |t|
t.integer "label_id"
t.integer "target_id"
t.string "target_type"
@@ -1121,7 +1121,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree
end
- create_table "label_priorities", force: :cascade do |t|
+ create_table "label_priorities", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "label_id", null: false
t.integer "priority", null: false
@@ -1132,7 +1132,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id", "label_id"], name: "index_label_priorities_on_project_id_and_label_id", unique: true, using: :btree
end
- create_table "labels", force: :cascade do |t|
+ create_table "labels", id: :serial, force: :cascade do |t|
t.string "title"
t.string "color"
t.integer "project_id"
@@ -1151,7 +1151,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree
end
- create_table "lfs_file_locks", force: :cascade do |t|
+ create_table "lfs_file_locks", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "user_id", null: false
t.datetime "created_at", null: false
@@ -1160,7 +1160,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_lfs_file_locks_on_user_id", using: :btree
end
- create_table "lfs_objects", force: :cascade do |t|
+ create_table "lfs_objects", id: :serial, force: :cascade do |t|
t.string "oid", null: false
t.bigint "size", null: false
t.datetime "created_at"
@@ -1171,7 +1171,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree
end
- create_table "lfs_objects_projects", force: :cascade do |t|
+ create_table "lfs_objects_projects", id: :serial, force: :cascade do |t|
t.integer "lfs_object_id", null: false
t.integer "project_id", null: false
t.datetime "created_at"
@@ -1179,7 +1179,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree
end
- create_table "lists", force: :cascade do |t|
+ create_table "lists", id: :serial, force: :cascade do |t|
t.integer "board_id", null: false
t.integer "label_id"
t.integer "list_type", default: 1, null: false
@@ -1191,7 +1191,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["list_type"], name: "index_lists_on_list_type", using: :btree
end
- create_table "members", force: :cascade do |t|
+ create_table "members", id: :serial, force: :cascade do |t|
t.integer "access_level", null: false
t.integer "source_id", null: false
t.string "source_type", null: false
@@ -1254,7 +1254,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["merge_request_diff_id", "relative_order"], name: "index_merge_request_diff_files_on_mr_diff_id_and_order", unique: true, using: :btree
end
- create_table "merge_request_diffs", force: :cascade do |t|
+ create_table "merge_request_diffs", id: :serial, force: :cascade do |t|
t.string "state"
t.integer "merge_request_id", null: false
t.datetime "created_at"
@@ -1271,7 +1271,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["merge_request_id", "id"], name: "index_merge_request_diffs_on_merge_request_id_and_id_partial", where: "((NOT stored_externally) OR (stored_externally IS NULL))", using: :btree
end
- create_table "merge_request_metrics", force: :cascade do |t|
+ create_table "merge_request_metrics", id: :serial, force: :cascade do |t|
t.integer "merge_request_id", null: false
t.datetime "latest_build_started_at"
t.datetime "latest_build_finished_at"
@@ -1292,7 +1292,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["pipeline_id"], name: "index_merge_request_metrics_on_pipeline_id", using: :btree
end
- create_table "merge_requests", force: :cascade do |t|
+ create_table "merge_requests", id: :serial, force: :cascade do |t|
t.string "target_branch", null: false
t.string "source_branch", null: false
t.integer "source_project_id"
@@ -1350,7 +1350,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["updated_by_id"], name: "index_merge_requests_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree
end
- create_table "merge_requests_closing_issues", force: :cascade do |t|
+ create_table "merge_requests_closing_issues", id: :serial, force: :cascade do |t|
t.integer "merge_request_id", null: false
t.integer "issue_id", null: false
t.datetime "created_at", null: false
@@ -1359,7 +1359,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["merge_request_id"], name: "index_merge_requests_closing_issues_on_merge_request_id", using: :btree
end
- create_table "milestones", force: :cascade do |t|
+ create_table "milestones", id: :serial, force: :cascade do |t|
t.string "title", null: false
t.integer "project_id"
t.text "description"
@@ -1381,7 +1381,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
end
- create_table "namespaces", force: :cascade do |t|
+ create_table "namespaces", id: :serial, force: :cascade do |t|
t.string "name", null: false
t.string "path", null: false
t.integer "owner_id"
@@ -1416,7 +1416,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["type"], name: "index_namespaces_on_type", using: :btree
end
- create_table "note_diff_files", force: :cascade do |t|
+ create_table "note_diff_files", id: :serial, force: :cascade do |t|
t.integer "diff_note_id", null: false
t.text "diff", null: false
t.boolean "new_file", null: false
@@ -1429,7 +1429,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["diff_note_id"], name: "index_note_diff_files_on_diff_note_id", unique: true, using: :btree
end
- create_table "notes", force: :cascade do |t|
+ create_table "notes", id: :serial, force: :cascade do |t|
t.text "note"
t.string "noteable_type"
t.integer "author_id"
@@ -1464,7 +1464,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
end
- create_table "notification_settings", force: :cascade do |t|
+ create_table "notification_settings", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "source_id"
t.string "source_type"
@@ -1490,7 +1490,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_notification_settings_on_user_id", using: :btree
end
- create_table "oauth_access_grants", force: :cascade do |t|
+ create_table "oauth_access_grants", id: :serial, force: :cascade do |t|
t.integer "resource_owner_id", null: false
t.integer "application_id", null: false
t.string "token", null: false
@@ -1502,7 +1502,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
end
- create_table "oauth_access_tokens", force: :cascade do |t|
+ create_table "oauth_access_tokens", id: :serial, force: :cascade do |t|
t.integer "resource_owner_id"
t.integer "application_id"
t.string "token", null: false
@@ -1516,7 +1516,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
end
- create_table "oauth_applications", force: :cascade do |t|
+ create_table "oauth_applications", id: :serial, force: :cascade do |t|
t.string "name", null: false
t.string "uid", null: false
t.string "secret", null: false
@@ -1531,13 +1531,13 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
end
- create_table "oauth_openid_requests", force: :cascade do |t|
+ create_table "oauth_openid_requests", id: :serial, force: :cascade do |t|
t.integer "access_grant_id", null: false
t.string "nonce", null: false
t.index ["access_grant_id"], name: "index_oauth_openid_requests_on_access_grant_id", using: :btree
end
- create_table "pages_domains", force: :cascade do |t|
+ create_table "pages_domains", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.text "certificate"
t.text "encrypted_key"
@@ -1548,6 +1548,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.string "verification_code", null: false
t.datetime_with_timezone "enabled_until"
t.datetime_with_timezone "remove_at"
+ t.boolean "auto_ssl_enabled", default: false, null: false
t.index ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree
t.index ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until", using: :btree
t.index ["project_id"], name: "index_pages_domains_on_project_id", using: :btree
@@ -1556,7 +1557,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["verified_at"], name: "index_pages_domains_on_verified_at", using: :btree
end
- create_table "personal_access_tokens", force: :cascade do |t|
+ create_table "personal_access_tokens", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.string "name", null: false
t.boolean "revoked", default: false
@@ -1570,7 +1571,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree
end
- create_table "pool_repositories", id: :bigserial, force: :cascade do |t|
+ create_table "pool_repositories", force: :cascade do |t|
t.integer "shard_id", null: false
t.string "disk_path"
t.string "state"
@@ -1580,7 +1581,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["source_project_id"], name: "index_pool_repositories_on_source_project_id", unique: true, using: :btree
end
- create_table "programming_languages", force: :cascade do |t|
+ create_table "programming_languages", id: :serial, force: :cascade do |t|
t.string "name", null: false
t.string "color", null: false
t.datetime_with_timezone "created_at", null: false
@@ -1595,7 +1596,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id", "project_id", "access_level"], name: "index_project_authorizations_on_user_id_project_id_access_level", unique: true, using: :btree
end
- create_table "project_auto_devops", force: :cascade do |t|
+ create_table "project_auto_devops", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
@@ -1605,14 +1606,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_project_auto_devops_on_project_id", unique: true, using: :btree
end
- create_table "project_ci_cd_settings", force: :cascade do |t|
+ create_table "project_ci_cd_settings", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.boolean "group_runners_enabled", default: true, null: false
t.boolean "merge_pipelines_enabled"
t.index ["project_id"], name: "index_project_ci_cd_settings_on_project_id", unique: true, using: :btree
end
- create_table "project_custom_attributes", force: :cascade do |t|
+ create_table "project_custom_attributes", id: :serial, force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "project_id", null: false
@@ -1622,14 +1623,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id", "key"], name: "index_project_custom_attributes_on_project_id_and_key", unique: true, using: :btree
end
- create_table "project_daily_statistics", id: :bigserial, force: :cascade do |t|
+ create_table "project_daily_statistics", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "fetch_count", null: false
t.date "date"
t.index ["project_id", "date"], name: "index_project_daily_statistics_on_project_id_and_date", unique: true, order: { date: :desc }, using: :btree
end
- create_table "project_deploy_tokens", force: :cascade do |t|
+ create_table "project_deploy_tokens", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "deploy_token_id", null: false
t.datetime_with_timezone "created_at", null: false
@@ -1637,7 +1638,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id", "deploy_token_id"], name: "index_project_deploy_tokens_on_project_id_and_deploy_token_id", unique: true, using: :btree
end
- create_table "project_error_tracking_settings", primary_key: "project_id", id: :integer, force: :cascade do |t|
+ create_table "project_error_tracking_settings", primary_key: "project_id", id: :integer, default: nil, force: :cascade do |t|
t.boolean "enabled", default: false, null: false
t.string "api_url"
t.string "encrypted_token"
@@ -1646,7 +1647,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.string "organization_name"
end
- create_table "project_features", force: :cascade do |t|
+ create_table "project_features", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "merge_requests_access_level"
t.integer "issues_access_level"
@@ -1660,7 +1661,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
end
- create_table "project_group_links", force: :cascade do |t|
+ create_table "project_group_links", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "group_id", null: false
t.datetime "created_at"
@@ -1671,7 +1672,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_project_group_links_on_project_id", using: :btree
end
- create_table "project_import_data", force: :cascade do |t|
+ create_table "project_import_data", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.text "data"
t.text "encrypted_credentials"
@@ -1680,7 +1681,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_project_import_data_on_project_id", using: :btree
end
- create_table "project_mirror_data", force: :cascade do |t|
+ create_table "project_mirror_data", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.string "status"
t.string "jid"
@@ -1690,7 +1691,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["status"], name: "index_project_mirror_data_on_status", using: :btree
end
- create_table "project_repositories", id: :bigserial, force: :cascade do |t|
+ create_table "project_repositories", force: :cascade do |t|
t.integer "shard_id", null: false
t.string "disk_path", null: false
t.integer "project_id", null: false
@@ -1699,7 +1700,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["shard_id"], name: "index_project_repositories_on_shard_id", using: :btree
end
- create_table "project_statistics", force: :cascade do |t|
+ create_table "project_statistics", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "namespace_id", null: false
t.bigint "commit_count", default: 0, null: false
@@ -1711,7 +1712,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_project_statistics_on_project_id", unique: true, using: :btree
end
- create_table "projects", force: :cascade do |t|
+ create_table "projects", id: :serial, force: :cascade do |t|
t.string "name"
t.string "path"
t.text "description"
@@ -1791,7 +1792,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
end
- create_table "prometheus_metrics", force: :cascade do |t|
+ create_table "prometheus_metrics", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.string "title", null: false
t.string "query", null: false
@@ -1809,7 +1810,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_prometheus_metrics_on_project_id", using: :btree
end
- create_table "protected_branch_merge_access_levels", force: :cascade do |t|
+ create_table "protected_branch_merge_access_levels", id: :serial, force: :cascade do |t|
t.integer "protected_branch_id", null: false
t.integer "access_level", default: 40, null: false
t.datetime "created_at", null: false
@@ -1817,7 +1818,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["protected_branch_id"], name: "index_protected_branch_merge_access", using: :btree
end
- create_table "protected_branch_push_access_levels", force: :cascade do |t|
+ create_table "protected_branch_push_access_levels", id: :serial, force: :cascade do |t|
t.integer "protected_branch_id", null: false
t.integer "access_level", default: 40, null: false
t.datetime "created_at", null: false
@@ -1825,7 +1826,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["protected_branch_id"], name: "index_protected_branch_push_access", using: :btree
end
- create_table "protected_branches", force: :cascade do |t|
+ create_table "protected_branches", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at"
@@ -1833,7 +1834,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
end
- create_table "protected_tag_create_access_levels", force: :cascade do |t|
+ create_table "protected_tag_create_access_levels", id: :serial, force: :cascade do |t|
t.integer "protected_tag_id", null: false
t.integer "access_level", default: 40
t.integer "user_id"
@@ -1845,7 +1846,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_protected_tag_create_access_levels_on_user_id", using: :btree
end
- create_table "protected_tags", force: :cascade do |t|
+ create_table "protected_tags", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at", null: false
@@ -1866,7 +1867,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["event_id"], name: "index_push_event_payloads_on_event_id", unique: true, using: :btree
end
- create_table "redirect_routes", force: :cascade do |t|
+ create_table "redirect_routes", id: :serial, force: :cascade do |t|
t.integer "source_id", null: false
t.string "source_type", null: false
t.string "path", null: false
@@ -1876,7 +1877,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["source_type", "source_id"], name: "index_redirect_routes_on_source_type_and_source_id", using: :btree
end
- create_table "release_links", id: :bigserial, force: :cascade do |t|
+ create_table "release_links", force: :cascade do |t|
t.integer "release_id", null: false
t.string "url", null: false
t.string "name", null: false
@@ -1886,7 +1887,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["release_id", "url"], name: "index_release_links_on_release_id_and_url", unique: true, using: :btree
end
- create_table "releases", force: :cascade do |t|
+ create_table "releases", id: :serial, force: :cascade do |t|
t.string "tag"
t.text "description"
t.integer "project_id"
@@ -1902,7 +1903,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id"], name: "index_releases_on_project_id", using: :btree
end
- create_table "remote_mirrors", force: :cascade do |t|
+ create_table "remote_mirrors", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.string "url"
t.boolean "enabled", default: false
@@ -1930,7 +1931,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["project_id", "programming_language_id"], name: "index_repository_languages_on_project_and_languages_id", unique: true, using: :btree
end
- create_table "resource_label_events", id: :bigserial, force: :cascade do |t|
+ create_table "resource_label_events", force: :cascade do |t|
t.integer "action", null: false
t.integer "issue_id"
t.integer "merge_request_id"
@@ -1946,7 +1947,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_resource_label_events_on_user_id", using: :btree
end
- create_table "routes", force: :cascade do |t|
+ create_table "routes", id: :serial, force: :cascade do |t|
t.integer "source_id", null: false
t.string "source_type", null: false
t.string "path", null: false
@@ -1958,7 +1959,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree
end
- create_table "sent_notifications", force: :cascade do |t|
+ create_table "sent_notifications", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.integer "noteable_id"
t.string "noteable_type"
@@ -1972,7 +1973,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree
end
- create_table "services", force: :cascade do |t|
+ create_table "services", id: :serial, force: :cascade do |t|
t.string "type"
t.string "title"
t.integer "project_id"
@@ -1999,12 +2000,12 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["type"], name: "index_services_on_type", using: :btree
end
- create_table "shards", force: :cascade do |t|
+ create_table "shards", id: :serial, force: :cascade do |t|
t.string "name", null: false
t.index ["name"], name: "index_shards_on_name", unique: true, using: :btree
end
- create_table "snippets", force: :cascade do |t|
+ create_table "snippets", id: :serial, force: :cascade do |t|
t.string "title"
t.text "content"
t.integer "author_id", null: false
@@ -2027,7 +2028,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
end
- create_table "spam_logs", force: :cascade do |t|
+ create_table "spam_logs", id: :serial, force: :cascade do |t|
t.integer "user_id"
t.string "source_ip"
t.string "user_agent"
@@ -2041,7 +2042,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.boolean "recaptcha_verified", default: false, null: false
end
- create_table "subscriptions", force: :cascade do |t|
+ create_table "subscriptions", id: :serial, force: :cascade do |t|
t.integer "user_id"
t.integer "subscribable_id"
t.string "subscribable_type"
@@ -2053,7 +2054,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["subscribable_id", "subscribable_type", "user_id", "project_id"], name: "index_subscriptions_on_subscribable_and_user_id_and_project_id", unique: true, using: :btree
end
- create_table "suggestions", id: :bigserial, force: :cascade do |t|
+ create_table "suggestions", force: :cascade do |t|
t.integer "note_id", null: false
t.integer "relative_order", limit: 2, null: false
t.boolean "applied", default: false, null: false
@@ -2066,7 +2067,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["note_id", "relative_order"], name: "index_suggestions_on_note_id_and_relative_order", unique: true, using: :btree
end
- create_table "system_note_metadata", force: :cascade do |t|
+ create_table "system_note_metadata", id: :serial, force: :cascade do |t|
t.integer "note_id", null: false
t.integer "commit_count"
t.string "action"
@@ -2075,7 +2076,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["note_id"], name: "index_system_note_metadata_on_note_id", unique: true, using: :btree
end
- create_table "taggings", force: :cascade do |t|
+ create_table "taggings", id: :serial, force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
@@ -2089,14 +2090,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["taggable_id", "taggable_type"], name: "index_taggings_on_taggable_id_and_taggable_type", using: :btree
end
- create_table "tags", force: :cascade do |t|
+ create_table "tags", id: :serial, force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
t.index ["name"], name: "index_tags_on_name", unique: true, using: :btree
t.index ["name"], name: "index_tags_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
end
- create_table "term_agreements", force: :cascade do |t|
+ create_table "term_agreements", id: :serial, force: :cascade do |t|
t.integer "term_id", null: false
t.integer "user_id", null: false
t.boolean "accepted", default: false, null: false
@@ -2107,7 +2108,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_term_agreements_on_user_id", using: :btree
end
- create_table "timelogs", force: :cascade do |t|
+ create_table "timelogs", id: :serial, force: :cascade do |t|
t.integer "time_spent", null: false
t.integer "user_id"
t.datetime "created_at", null: false
@@ -2120,7 +2121,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_timelogs_on_user_id", using: :btree
end
- create_table "todos", force: :cascade do |t|
+ create_table "todos", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "project_id"
t.integer "target_id"
@@ -2144,12 +2145,12 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_todos_on_user_id", using: :btree
end
- create_table "trending_projects", force: :cascade do |t|
+ create_table "trending_projects", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.index ["project_id"], name: "index_trending_projects_on_project_id", unique: true, using: :btree
end
- create_table "u2f_registrations", force: :cascade do |t|
+ create_table "u2f_registrations", id: :serial, force: :cascade do |t|
t.text "certificate"
t.string "key_handle"
t.string "public_key"
@@ -2162,7 +2163,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_u2f_registrations_on_user_id", using: :btree
end
- create_table "uploads", force: :cascade do |t|
+ create_table "uploads", id: :serial, force: :cascade do |t|
t.bigint "size", null: false
t.string "path", limit: 511, null: false
t.string "checksum", limit: 64
@@ -2179,7 +2180,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["uploader", "path"], name: "index_uploads_on_uploader_and_path", using: :btree
end
- create_table "user_agent_details", force: :cascade do |t|
+ create_table "user_agent_details", id: :serial, force: :cascade do |t|
t.string "user_agent", null: false
t.string "ip_address", null: false
t.integer "subject_id", null: false
@@ -2190,14 +2191,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["subject_id", "subject_type"], name: "index_user_agent_details_on_subject_id_and_subject_type", using: :btree
end
- create_table "user_callouts", force: :cascade do |t|
+ create_table "user_callouts", id: :serial, force: :cascade do |t|
t.integer "feature_name", null: false
t.integer "user_id", null: false
t.index ["user_id", "feature_name"], name: "index_user_callouts_on_user_id_and_feature_name", unique: true, using: :btree
t.index ["user_id"], name: "index_user_callouts_on_user_id", using: :btree
end
- create_table "user_custom_attributes", force: :cascade do |t|
+ create_table "user_custom_attributes", id: :serial, force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "user_id", null: false
@@ -2214,7 +2215,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_user_interacted_projects_on_user_id", using: :btree
end
- create_table "user_preferences", force: :cascade do |t|
+ create_table "user_preferences", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "issue_notes_filter", limit: 2, default: 0, null: false
t.integer "merge_request_notes_filter", limit: 2, default: 0, null: false
@@ -2226,7 +2227,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true, using: :btree
end
- create_table "user_statuses", primary_key: "user_id", force: :cascade do |t|
+ create_table "user_statuses", primary_key: "user_id", id: :serial, force: :cascade do |t|
t.integer "cached_markdown_version"
t.string "emoji", default: "speech_balloon", null: false
t.string "message", limit: 100
@@ -2234,7 +2235,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_user_statuses_on_user_id", using: :btree
end
- create_table "user_synced_attributes_metadata", force: :cascade do |t|
+ create_table "user_synced_attributes_metadata", id: :serial, force: :cascade do |t|
t.boolean "name_synced", default: false
t.boolean "email_synced", default: false
t.boolean "location_synced", default: false
@@ -2243,7 +2244,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id"], name: "index_user_synced_attributes_metadata_on_user_id", unique: true, using: :btree
end
- create_table "users", force: :cascade do |t|
+ create_table "users", id: :serial, force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
@@ -2329,7 +2330,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"}
end
- create_table "users_star_projects", force: :cascade do |t|
+ create_table "users_star_projects", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "user_id", null: false
t.datetime "created_at"
@@ -2338,7 +2339,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["user_id", "project_id"], name: "index_users_star_projects_on_user_id_and_project_id", unique: true, using: :btree
end
- create_table "web_hook_logs", force: :cascade do |t|
+ create_table "web_hook_logs", id: :serial, force: :cascade do |t|
t.integer "web_hook_id", null: false
t.string "trigger"
t.string "url"
@@ -2355,7 +2356,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do
t.index ["web_hook_id"], name: "index_web_hook_logs_on_web_hook_id", using: :btree
end
- create_table "web_hooks", force: :cascade do |t|
+ create_table "web_hooks", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
diff --git a/doc/ci/large_repositories/index.md b/doc/ci/large_repositories/index.md
index cfe638c0a22..244ccbb92b0 100644
--- a/doc/ci/large_repositories/index.md
+++ b/doc/ci/large_repositories/index.md
@@ -72,8 +72,9 @@ done by GitLab, requiring you to do them.
> Introduced in GitLab Runner 11.10.
-`GIT_CLONE_PATH` allows you to control where you clone your sources.
-This can have implications if you heavily use big repositories with fork workflow.
+[`GIT_CLONE_PATH`](../yaml/README.md#custom-build-directories) allows you to
+control where you clone your sources. This can have implications if you
+heavily use big repositories with fork workflow.
Fork workflow from GitLab Runner's perspective is stored as a separate repository
with separate worktree. That means that GitLab Runner cannot optimize the usage
@@ -83,29 +84,31 @@ In such cases, ideally you want to make the GitLab Runner executor be used only
for the given project and not shared across different projects to make this
process more efficient.
-The `GIT_CLONE_PATH` has to be within the `$CI_BUILDS_DIR`. Currently,
-it is impossible to pick any path from disk.
+The [`GIT_CLONE_PATH`](../yaml/README.md#custom-build-directories) has to be
+within the `$CI_BUILDS_DIR`. Currently, it is impossible to pick any path
+from disk.
## Git clean flags
> Introduced in GitLab Runner 11.10.
-`GIT_CLEAN_FLAGS` allows you to control whether or not you require
-the `git clean` command to be executed for each CI job.
-By default, GitLab ensures that you have your worktree on the given SHA,
+[`GIT_CLEAN_FLAGS`](../yaml/README.md#git-clean-flags) allows you to control
+whether or not you require the `git clean` command to be executed for each CI
+job. By default, GitLab ensures that you have your worktree on the given SHA,
and that your repository is clean.
-`GIT_CLEAN_FLAGS` is disabled when set to `none`. On very big repositories, this
-might be desired because `git clean` is disk I/O intensive. Controlling that
-with `GIT_CLEAN_FLAGS: -ffdx -e .build/`, for example, allows you to control and
-disable removal of some directories within the worktree between subsequent runs,
-which can speed-up the incremental builds. This has the biggest effect
-if you re-use existing machines, and have an existing worktree that you can re-use
-for builds.
-
-For exact parameters accepted by `GIT_CLEAN_FLAGS`, see the documentation
-for [git clean](https://git-scm.com/docs/git-clean). The
-available parameters are dependent on Git version.
+[`GIT_CLEAN_FLAGS`](../yaml/README.md#git-clean-flags) is disabled when set
+to `none`. On very big repositories, this might be desired because `git
+clean` is disk I/O intensive. Controlling that with `GIT_CLEAN_FLAGS: -ffdx
+-e .build/`, for example, allows you to control and disable removal of some
+directories within the worktree between subsequent runs, which can speed-up
+the incremental builds. This has the biggest effect if you re-use existing
+machines, and have an existing worktree that you can re-use for builds.
+
+For exact parameters accepted by
+[`GIT_CLEAN_FLAGS`](../yaml/README.md#git-clean-flags), see the documentation
+for [git clean](https://git-scm.com/docs/git-clean). The available parameters
+are dependent on Git version.
## Fork-based workflow
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 830f015a108..9983b015b31 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -434,8 +434,9 @@ Below you can find supported syntax reference:
1. Equality matching using a string
> Example: `$VARIABLE == "some value"`
+ > Example: `$VARIABLE != "some value"` _(added in 11.11)_
- You can use equality operator `==` to compare a variable content to a
+ You can use equality operator `==` or `!=` to compare a variable content to a
string. We support both, double quotes and single quotes to define a string
value, so both `$VARIABLE == "some value"` and `$VARIABLE == 'some value'`
are supported. `"some value" == $VARIABLE` is correct too.
@@ -443,22 +444,26 @@ Below you can find supported syntax reference:
1. Checking for an undefined value
> Example: `$VARIABLE == null`
+ > Example: `$VARIABLE != null` _(added in 11.11)_
It sometimes happens that you want to check whether a variable is defined
or not. To do that, you can compare a variable to `null` keyword, like
`$VARIABLE == null`. This expression is going to evaluate to truth if
- variable is not defined.
+ variable is not defined when `==` is used, or to falsey if `!=` is used.
1. Checking for an empty variable
> Example: `$VARIABLE == ""`
+ > Example: `$VARIABLE != ""` _(added in 11.11)_
If you want to check whether a variable is defined, but is empty, you can
- simply compare it against an empty string, like `$VAR == ''`.
+ simply compare it against an empty string, like `$VAR == ''` or non-empty
+ string `$VARIABLE != ""`.
1. Comparing two variables
> Example: `$VARIABLE_1 == $VARIABLE_2`
+ > Example: `$VARIABLE_1 != $VARIABLE_2` _(added in 11.11)_
It is possible to compare two variables. This is going to compare values
of these variables.
@@ -477,9 +482,11 @@ Below you can find supported syntax reference:
1. Pattern matching _(added in 11.0)_
> Example: `$VARIABLE =~ /^content.*/`
+ > Example: `$VARIABLE_1 !~ /^content.*/` _(added in 11.11)_
It is possible perform pattern matching against a variable and regular
- expression. Expression like this evaluates to truth if matches are found.
+ expression. Expression like this evaluates to truth if matches are found
+ when using `=~`. It evaluates to truth if matches are not found when `!~` is used.
Pattern matching is case-sensitive by default. Use `i` flag modifier, like
`/pattern/i` to make a pattern case-insensitive.
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 846c539daab..40458137752 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -49,23 +49,23 @@ future GitLab releases.**
| `CI_JOB_STAGE` | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
| `CI_JOB_TOKEN` | 9.0 | 1.2 | Token used for authenticating with the [GitLab Container Registry][registry] and downloading [dependent repositories][dependent-repositories] |
| `CI_JOB_URL` | 11.1 | 0.5 | Job details URL |
-| `CI_MERGE_REQUEST_ID` | 11.6 | all | The ID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_IID` | 11.6 | all | The IID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `namespace/awesome-project`) |
-| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `http://192.168.10.15:3000/namespace/awesome-project`) |
-| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). (e.g. `refs/merge-requests/1/head`) |
-| `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` | 11.6 | all | The source branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` | 11.9 | all | The HEAD sha of the source branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_SOURCE_PROJECT_ID` | 11.6 | all | The ID of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` | 11.6 | all | The path of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_SOURCE_PROJECT_URL` | 11.6 | all | The URL of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` | 11.6 | all | The target branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` | 11.9 | all | The HEAD sha of the target branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_TITLE` | 11.9 | all | The title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of usernames of assignees for the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). [Multiple assignees for merge requests](https://gitlab.com/gitlab-org/gitlab-ee/issues/2004) is scheduled for a future release |
-| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
-| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| `CI_MERGE_REQUEST_ID` | 11.6 | all | The ID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_IID` | 11.6 | all | The IID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `namespace/awesome-project`). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `http://192.168.10.15:3000/namespace/awesome-project`). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). (e.g. `refs/merge-requests/1/head`). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` | 11.6 | all | The source branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` | 11.9 | all | The HEAD sha of the source branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_SOURCE_PROJECT_ID` | 11.6 | all | The ID of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` | 11.6 | all | The path of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_SOURCE_PROJECT_URL` | 11.6 | all | The URL of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` | 11.6 | all | The target branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` | 11.9 | all | The HEAD sha of the target branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_TITLE` | 11.9 | all | The title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of username(s) of assignee(s) for the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
+| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. |
| `CI_NODE_INDEX` | 11.5 | all | Index of the job in the job set. If the job is not parallelized, this variable is not set. |
| `CI_NODE_TOTAL` | 11.5 | all | Total number of instances of this job running in parallel. If the job is not parallelized, this variable is set to `1`. |
| `CI_API_V4_URL` | 11.7 | all | The GitLab API v4 root URL |
@@ -84,6 +84,7 @@ future GitLab releases.**
| `CI_PROJECT_PATH_SLUG` | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
| `CI_PROJECT_URL` | 8.10 | 0.5 | The HTTP(S) address to access project |
| `CI_PROJECT_VISIBILITY` | 10.3 | all | The project visibility (internal, private, public) |
+| `CI_COMMIT_REF_PROTECTED` | 11.11 | all | If the job is running on a protected branch |
| `CI_REGISTRY` | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry |
| `CI_REGISTRY_IMAGE` | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project |
| `CI_REGISTRY_PASSWORD` | 9.0 | all | The password to use to push containers to the GitLab Container Registry |
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index 827b3d7681a..0e1ab8663ed 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -340,13 +340,14 @@ addressed.
In order to track things that can be improved in GitLab's codebase,
we use the ~"technical debt" label in [GitLab's issue tracker][ce-tracker].
-For user experience improvements, we use the ~"UX debt" label.
+For missed user experience requirements, we use the ~"UX debt" label.
These labels should be added to issues that describe things that can be improved,
shortcuts that have been taken, features that need additional attention, and all
other things that have been left behind due to high velocity of development.
For example, code that needs refactoring should use the ~"technical debt" label,
-user experience refinements should use the ~"UX debt" label.
+something that didn't ship according to our Design System guidelines should
+use the ~"UX debt" label.
Everyone can create an issue, though you may need to ask for adding a specific
label, if you do not have permissions to do it by yourself. Additional labels
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index ffb89848edb..fbca99fbfea 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -6,7 +6,7 @@ description: 'Writing styles, markup, formatting, and other standards for GitLab
This document defines the standards for GitLab's documentation content and files.
-For broader information about the documentation, see the [Documentation guidelines](index.md).
+For broader information about the documentation, see the [Documentation guidelines](index.md).
For programmatic help adhering to the guidelines, see [linting](index.md#linting).
@@ -38,7 +38,7 @@ Include any media types/sources if the content is relevant to readers. You can f
- If you use an image that has a separate source file (for example, a vector or diagram format), link the image to the source file so that it may be reused or updated by anyone.
- Do not copy and paste content from other sources unless it is a limited quotation with the source cited. Typically it is better to either rephrase relevant information in your own words or link out to the other source.
-
+
### No special types
In the software industry, it is a best practice to organize documentatioin in different types. For example, [Divio recommends](https://www.divio.com/blog/documentation/):
@@ -77,7 +77,7 @@ and cross-link between any related content.
We employ a **docs-first methodology** to help ensure that the docs remain a complete and trusted resource, and to make communicating about the use of GitLab more efficient.
* If the answer to a question exists in documentation, share the link to the docs instead of rephrasing the information.
-* When you encounter new information not available in GitLab’s documentation (for example, when working on a support case or testing a feature), your first step should be to create a merge request to add this information to the docs. You can then share the MR in order to communicate this information.
+* When you encounter new information not available in GitLab’s documentation (for example, when working on a support case or testing a feature), your first step should be to create a merge request to add this information to the docs. You can then share the MR in order to communicate this information.
New information that would be useful toward the future usage or troubleshooting of GitLab should not be written directly in a forum or other messaging system, but added to a docs MR and then referenced, as described above. Note that among any other doc changes, you can always add a Troubleshooting section to a doc if none exists, or un-comment and use the placeholder Troubleshooting section included as part of our [doc template](structure.md#template-for-new-docs), if present.
@@ -96,7 +96,7 @@ The [documentation website](https://docs.gitlab.com) uses GitLab Kramdown as its
The [`gitlab-kramdown`](https://gitlab.com/gitlab-org/gitlab_kramdown)
Ruby gem will support all [GFM markup](../../user/markdown.md) in the future. That is,
all markup that is supported for display in the GitLab application itself. For now,
-use regular Markdown markup, following the rules in the linked style guide.
+use regular Markdown markup, following the rules in the linked style guide.
Note that Kramdown-specific markup (e.g., `{:.class}`) will not render properly on GitLab instances under [`/help`](index.md#gitlab-help).
@@ -652,6 +652,7 @@ keyword "only":
- For GitLab Ultimate: `**[ULTIMATE ONLY]**`.
For GitLab.com only tiers (when the feature is not available for self-hosted instances):
+
- For GitLab Bronze and higher tiers: `**[BRONZE ONLY]**`.
- For GitLab Silver and higher tiers: `**[SILVER ONLY]**`.
- For GitLab Gold: `**[GOLD ONLY]**`.
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 223585ebb55..38d56322de8 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -300,7 +300,7 @@ file in. Once the changes are on master, they will be picked up by
[Crowdin](http://translate.gitlab.com) and be presented for translation.
If there are merge conflicts in the `gitlab.pot` file, you can delete the file
-and regenerate it using the same command. Confirm that you are not deleting any strings accidentally by looking over the diff.
+and regenerate it using the same command.
### Validating PO files
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index 131e3edf35e..aa463e467d4 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -13,14 +13,17 @@ are very appreciative of the work done by translators and proofreaders!
- Lyubomir Vasilev - [Crowdin](https://crowdin.com/profile/lyubomirv)
- Catalan
- David Planella - [GitLab](https://gitlab.com/dplanella), [Crowdin](https://crowdin.com/profile/dplanella)
-- Chinese Simplified
- - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
-- Chinese Traditional
+- Chinese Simplified 简体中文
- Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
+ - Victor Wu - [GitLab](https://gitlab.com/victorwuky), [Crowdin](https://crowdin.com/profile/victorwu)
+ - Xiaogang Wen - [GitLab](https://gitlab.com/xiaogang_gitlab), [Crowdin](https://crowdin.com/profile/xiaogang_gitlab)
+- Chinese Traditional 繁體中文
- Weizhe Ding - [GitLab](https://gitlab.com/d.weizhe), [Crowdin](https://crowdin.com/profile/d.weizhe)
- Yi-Jyun Pan - [GitLab](https://gitlab.com/pan93412), [Crowdin](https://crowdin.com/profile/pan93412)
-- Chinese Traditional, Hong Kong
- - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
+ - Victor Wu - [GitLab](https://gitlab.com/victorwuky), [Crowdin](https://crowdin.com/profile/victorwu)
+- Chinese Traditional, Hong Kong 繁體中文 (香港)
+ - Victor Wu - [GitLab](https://gitlab.com/victorwuky), [Crowdin](https://crowdin.com/profile/victorwu)
+ - Ivan Ip - [GitLab](https://gitlab.com/lifehome), [Crowdin](https://crowdin.com/profile/lifehome)
- Czech
- Proofreaders needed.
- Danish
@@ -56,7 +59,6 @@ are very appreciative of the work done by translators and proofreaders!
- Hiroyuki Sato - [GitLab](https://gitlab.com/hiroponz), [Crowdin](https://crowdin.com/profile/hiroponz)
- Korean
- Chang-Ho Cha - [GitLab](https://gitlab.com/changho-cha), [Crowdin](https://crowdin.com/profile/zzazang)
- - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
- Ji Hun Oh - [GitLab](https://gitlab.com/Baw-Appie), [Crowdin](https://crowdin.com/profile/BawAppie)
- Jeongwhan Choi - [GitLab](https://gitlab.com/jeongwhanchoi), [Crowdin](https://crowdin.com/profile/jeongwhanchoi)
- Mongolian
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index dcb32c89f65..1ae69127295 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -12,6 +12,22 @@ The `setup` task is an alias for `gitlab:setup`.
This tasks calls `db:reset` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and finally it calls `db:seed_fu` to seed the database.
Note: `db:setup` calls `db:seed` but this does nothing.
+### Seeding issues for all or a given project
+
+You can seed issues for all or a given project with the `gitlab:seed:issues`
+task:
+
+```shell
+# All projects
+bin/rake gitlab:seed:issues
+
+# A specific project
+bin/rake "gitlab:seed:issues[group-path/project-path]"
+```
+
+By default, this seeds an average of 2 issues per week for the last 5 weeks per
+project.
+
### Automation
If you're very sure that you want to **wipe the current database** and refill
diff --git a/doc/development/testing_guide/img/review_apps_cicd_architecture.png b/doc/development/testing_guide/img/review_apps_cicd_architecture.png
index 87e472076f3..1ee28d3db91 100644
--- a/doc/development/testing_guide/img/review_apps_cicd_architecture.png
+++ b/doc/development/testing_guide/img/review_apps_cicd_architecture.png
Binary files differ
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
index 55ca502f84a..7ad33f05f77 100644
--- a/doc/development/testing_guide/review_apps.md
+++ b/doc/development/testing_guide/review_apps.md
@@ -6,7 +6,7 @@ Review Apps are automatically deployed by each pipeline, both in
## How does it work?
-### CD/CD architecture diagram
+### CI/CD architecture diagram
![Review Apps CI/CD architecture](img/review_apps_cicd_architecture.png)
@@ -14,23 +14,29 @@ Review Apps are automatically deployed by each pipeline, both in
<summary>Show mermaid source</summary>
<pre>
graph TD
- B1 -.->|2. once gitlab:assets:compile is done,<br />triggers a CNG-mirror pipeline and wait for it to be done| A2
- C1 -.->|2. once review-build-cng is done,<br />Helm deploys the Review App using the Cloud<br/>Native images built by the CNG-mirror pipeline| A3
-
-subgraph gitlab-ce/ee `test` stage
- A1[gitlab:assets:compile]
- B1[review-build-cng] -->|1. wait for| A1
- C1[review-deploy] -->|1. wait for| B1
- D1[review-qa-smoke] -->|1. wait for| C1
- D1[review-qa-smoke] -.->|2. once review-deploy is done| E1>gitlab-qa runs the smoke<br/>suite against the Review App]
+ build-qa-image -.->|once the `prepare` stage is done| gitlab:assets:compile
+ review-build-cng -->|triggers a CNG-mirror pipeline and wait for it to be done| CNG-mirror
+ review-build-cng -.->|once the `test` stage is done| review-deploy
+ review-deploy -.->|once the `review` stage is done| review-qa-smoke
+
+subgraph 1. gitlab-ce/ee `prepare` stage
+ build-qa-image
end
-subgraph CNG-mirror pipeline
- A2>Cloud Native images are built];
+subgraph 2. gitlab-ce/ee `test` stage
+ gitlab:assets:compile -->|plays dependent job once done| review-build-cng
end
-subgraph GCP `gitlab-review-apps` project
- A3>"Cloud Native images are deployed to the<br />`review-apps-ce` or `review-apps-ee` Kubernetes (GKE) cluster"];
+subgraph 3. gitlab-ce/ee `review` stage
+ review-deploy["review-deploy<br /><br />Helm deploys the Review App using the Cloud<br/>Native images built by the CNG-mirror pipeline.<br /><br />Cloud Native images are deployed to the `review-apps-ce` or `review-apps-ee`<br />Kubernetes (GKE) cluster, in the GCP `gitlab-review-apps` project."]
+ end
+
+subgraph 4. gitlab-ce/ee `qa` stage
+ review-qa-smoke[review-qa-smoke<br /><br />gitlab-qa runs the smoke suite against the Review App.]
+ end
+
+subgraph CNG-mirror pipeline
+ CNG-mirror>Cloud Native images are built];
end
</pre>
</details>
@@ -38,40 +44,37 @@ subgraph GCP `gitlab-review-apps` project
### Detailed explanation
1. On every [pipeline][gitlab-pipeline] during the `test` stage, the
- [`review-build-cng`][review-build-cng] and
- [`review-deploy`][review-deploy] jobs are automatically started.
- - The [`review-deploy`][review-deploy] job waits for the
- [`review-build-cng`][review-build-cng] job to finish.
- - The [`review-build-cng`][review-build-cng] job waits for the
- [`gitlab:assets:compile`][gitlab:assets:compile] job to finish since the
- [`CNG-mirror`][cng-mirror] pipeline triggered in the following step depends on it.
-1. Once the [`gitlab:assets:compile`][gitlab:assets:compile] job is done,
- [`review-build-cng`][review-build-cng] [triggers a pipeline][cng-pipeline]
- in the [`CNG-mirror`][cng-mirror] project.
- - The [`CNG-mirror`][cng-pipeline] pipeline creates the Docker images of
- each component (e.g. `gitlab-rails-ee`, `gitlab-shell`, `gitaly` etc.)
- based on the commit from the [GitLab pipeline][gitlab-pipeline] and store
- them in its [registry][cng-mirror-registry].
- - We use the [`CNG-mirror`][cng-mirror] project so that the `CNG`, (**C**loud
- **N**ative **G**itLab), project's registry is not overloaded with a
- lot of transient Docker images.
-1. Once the [`review-build-cng`][review-build-cng] job is done, the
- [`review-deploy`][review-deploy] job deploys the Review App using
- [the official GitLab Helm chart][helm-chart] to the
- [`review-apps-ce`][review-apps-ce] / [`review-apps-ee`][review-apps-ee]
- Kubernetes cluster on GCP.
- - The actual scripts used to deploy the Review App can be found at
- [`scripts/review_apps/review-apps.sh`][review-apps.sh].
- - These scripts are basically
- [our official Auto DevOps scripts][Auto-DevOps.gitlab-ci.yml] where the
- default CNG images are overridden with the images built and stored in the
- [`CNG-mirror` project's registry][cng-mirror-registry].
- - Since we're using [the official GitLab Helm chart][helm-chart], this means
- you get a dedicated environment for your branch that's very close to what
- it would look in production.
+ [`gitlab:assets:compile`][gitlab:assets:compile] job is automatically started.
+ - Once it's done, it starts the [`review-build-cng`][review-build-cng]
+ manual job since the [`CNG-mirror`][cng-mirror] pipeline triggered in the
+ following step depends on it.
+1. The [`review-build-cng`][review-build-cng] job [triggers a pipeline][cng-mirror-pipeline]
+ in the [`CNG-mirror`][cng-mirror] project.
+ - The [`CNG-mirror`][cng-mirror-pipeline] pipeline creates the Docker images of
+ each component (e.g. `gitlab-rails-ee`, `gitlab-shell`, `gitaly` etc.)
+ based on the commit from the [GitLab pipeline][gitlab-pipeline] and stores
+ them in its [registry][cng-mirror-registry].
+ - We use the [`CNG-mirror`][cng-mirror] project so that the `CNG`, (**C**loud
+ **N**ative **G**itLab), project's registry is not overloaded with a
+ lot of transient Docker images.
+ - Note that the official CNG images are built by the `cloud-native-image`
+ job, which runs only for tags, and triggers itself a [`CNG`][cng] pipeline.
+1. Once the `test` stage is done, the [`review-deploy`][review-deploy] job
+ deploys the Review App using [the official GitLab Helm chart][helm-chart] to
+ the [`review-apps-ce`][review-apps-ce] / [`review-apps-ee`][review-apps-ee]
+ Kubernetes cluster on GCP.
+ - The actual scripts used to deploy the Review App can be found at
+ [`scripts/review_apps/review-apps.sh`][review-apps.sh].
+ - These scripts are basically
+ [our official Auto DevOps scripts][Auto-DevOps.gitlab-ci.yml] where the
+ default CNG images are overridden with the images built and stored in the
+ [`CNG-mirror` project's registry][cng-mirror-registry].
+ - Since we're using [the official GitLab Helm chart][helm-chart], this means
+ you get a dedicated environment for your branch that's very close to what
+ it would look in production.
1. Once the [`review-deploy`][review-deploy] job succeeds, you should be able to
- use your Review App thanks to the direct link to it from the MR widget. To log
- into the Review App, see "Log into my Review App?" below.
+ use your Review App thanks to the direct link to it from the MR widget. To log
+ into the Review App, see "Log into my Review App?" below.
**Additional notes:**
@@ -82,71 +85,69 @@ subgraph GCP `gitlab-review-apps` project
- If the Review App deployment fails, you can simply retry it (there's no need
to run the [`review-stop`][gitlab-ci-yml] job first).
- The manual [`review-stop`][gitlab-ci-yml] in the `test` stage can be used to
- stop a Review App manually, and is also started by GitLab once a branch is
- deleted.
-- Review Apps are cleaned up regularly using a pipeline schedule that runs
+ stop a Review App manually, and is also started by GitLab once a merge
+ request's branch is deleted after being merged.
+- Review Apps are cleaned up regularly via a pipeline schedule that runs
the [`schedule:review-cleanup`][gitlab-ci-yml] job.
## QA runs
-On every [pipeline][gitlab-pipeline] during the `test` stage, the
-`review-qa-smoke` job is automatically started: it runs the QA smoke suite.
-You can also manually start the `review-qa-all`: it runs the QA full suite.
+On every [pipeline][gitlab-pipeline] in the `qa` stage (which comes after the
+`review` stage), the `review-qa-smoke` job is automatically started and it runs
+the QA smoke suite.
-Note that both jobs first wait for the `review-deploy` job to be finished.
+You can also manually start the `review-qa-all`: it runs the full QA suite.
## Performance Metrics
-On every [pipeline][gitlab-pipeline] during the `test` stage, the
+On every [pipeline][gitlab-pipeline] in the `qa` stage, the
`review-performance` job is automatically started: this job does basic
-browser performance testing using [Sitespeed.io Container](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html) .
+browser performance testing using a
+[Sitespeed.io Container](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html).
-This job waits for the `review-deploy` job to be finished.
+## How to:
-## How to?
-
-### Log into my Review App?
+### Log into my Review App
The default username is `root` and its password can be found in the 1Password
secure note named **gitlab-{ce,ee} Review App's root password**.
-### Enable a feature flag for my Review App?
+### Enable a feature flag for my Review App
1. Open your Review App and log in as documented above.
1. Create a personal access token.
1. Enable the feature flag using the [Feature flag API](../../api/features.md).
-### Find my Review App slug?
+### Find my Review App slug
1. Open the `review-deploy` job.
1. Look for `Checking for previous deployment of review-*`.
1. For instance for `Checking for previous deployment of review-qa-raise-e-12chm0`,
- your Review App slug would be `review-qa-raise-e-12chm0` in this case.
+ your Review App slug would be `review-qa-raise-e-12chm0` in this case.
-### Run a Rails console?
+### Run a Rails console
1. [Filter Workloads by your Review App slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps)
- , e.g. `review-29951-issu-id2qax`.
-1. Find and open the `task-runner` Deployment, e.g. `review-29951-issu-id2qax-task-runner`.
-1. Click on the Pod in the "Managed pods" section, e.g. `review-29951-issu-id2qax-task-runner-d5455cc8-2lsvz`.
+ , e.g. `review-qa-raise-e-12chm0`.
+1. Find and open the `task-runner` Deployment, e.g. `review-qa-raise-e-12chm0-task-runner`.
+1. Click on the Pod in the "Managed pods" section, e.g. `review-qa-raise-e-12chm0-task-runner-d5455cc8-2lsvz`.
1. Click on the `KUBECTL` dropdown, then `Exec` -> `task-runner`.
1. Replace `-c task-runner -- ls` with `-it -- gitlab-rails console` from the
- default command or
- - Run `kubectl exec --namespace review-apps-ce review-29951-issu-id2qax-task-runner-d5455cc8-2lsvz -it -- gitlab-rails console`
- and
- - Replace `review-apps-ce` with `review-apps-ee` if the Review App
- is running EE, and
- - Replace `review-29951-issu-id2qax-task-runner-d5455cc8-2lsvz`
- with your Pod's name.
+ default command or
+ - Run `kubectl exec --namespace review-apps-ce review-qa-raise-e-12chm0-task-runner-d5455cc8-2lsvz -it -- gitlab-rails console` and
+ - Replace `review-apps-ce` with `review-apps-ee` if the Review App
+ is running EE, and
+ - Replace `review-qa-raise-e-12chm0-task-runner-d5455cc8-2lsvz`
+ with your Pod's name.
-### Dig into a Pod's logs?
+### Dig into a Pod's logs
-1. [Filter Workloads by your Review App slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps)
- , e.g. `review-1979-1-mul-dnvlhv`.
+1. [Filter Workloads by your Review App slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps),
+ e.g. `review-qa-raise-e-12chm0`.
1. Find and open the `migrations` Deployment, e.g.
- `review-1979-1-mul-dnvlhv-migrations.1`.
+ `review-qa-raise-e-12chm0-migrations.1`.
1. Click on the Pod in the "Managed pods" section, e.g.
- `review-1979-1-mul-dnvlhv-migrations.1-nqwtx`.
+ `review-qa-raise-e-12chm0-migrations.1-nqwtx`.
1. Click on the `Container logs` link.
## Frequently Asked Questions
@@ -182,7 +183,8 @@ find a way to limit it to only us.**
[review-build-cng]: https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/149511623
[review-deploy]: https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/149511624
[cng-mirror]: https://gitlab.com/gitlab-org/build/CNG-mirror
-[cng-pipeline]: https://gitlab.com/gitlab-org/build/CNG-mirror/pipelines/44364657
+[cng]: https://gitlab.com/gitlab-org/build/CNG
+[cng-mirror-pipeline]: https://gitlab.com/gitlab-org/build/CNG-mirror/pipelines/44364657
[cng-mirror-registry]: https://gitlab.com/gitlab-org/build/CNG-mirror/container_registry
[helm-chart]: https://gitlab.com/charts/gitlab/
[review-apps-ce]: https://console.cloud.google.com/kubernetes/clusters/details/us-central1-a/review-apps-ce?project=gitlab-review-apps
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 0ab9406c681..1c559b5263c 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -737,6 +737,9 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-base-domain). By default, set automatically by the [Auto DevOps setting](#enablingdisabling-auto-devops). This variable is deprecated and [is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959). Use `KUBE_INGRESS_BASE_DOMAIN` instead. |
| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/charts/auto-deploy-app). |
| `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. |
+| `AUTO_DEVOPS_CHART_REPOSITORY_NAME` | From Gitlab 11.11, this variable can be used to set the name of the helm repository; defaults to "gitlab" |
+| `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | From Gitlab 11.11, this variable can be used to set a username to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD) |
+| `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From Gitlab 11.11, this variable can be used to set a password to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_USERNAME) |
| `REPLICAS` | The number of replicas to deploy; defaults to 1. |
| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. This takes precedence over `REPLICAS`; defaults to 1. |
| `CANARY_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html); defaults to 1 |
diff --git a/doc/user/project/integrations/gitlab_slack_application.md b/doc/user/project/integrations/gitlab_slack_application.md
new file mode 100644
index 00000000000..8e062ca627b
--- /dev/null
+++ b/doc/user/project/integrations/gitlab_slack_application.md
@@ -0,0 +1,65 @@
+# GitLab Slack application **[FREE ONLY]**
+
+NOTE: **Note:**
+The GitLab Slack application is only configurable for GitLab.com. It will **not**
+work for on-premises installations where you can configure the
+[Slack slash commands](slack_slash_commands.md) service instead. We're working
+with Slack on making this configurable for all GitLab installations, but there's
+no ETA.
+It was first introduced in GitLab 9.4 and distributed to Slack App Directory in
+GitLab 10.2.
+
+Slack provides a native application which you can enable via your project's
+integrations on GitLab.com.
+
+## Slack App Directory
+
+The simplest way to enable the GitLab Slack application for your workspace is to
+install the [GitLab application](https://slack-platform.slack.com/apps/A676ADMV5-gitlab) from
+the [Slack App Directory](https://slack.com/apps).
+
+Clicking install will take you to the
+[GitLab Slack application landing page](https://gitlab.com/profile/slack/edit)
+where you can select a project to enable the GitLab Slack application for.
+
+![GitLab Slack application landing page](img/gitlab_slack_app_landing_page.png)
+
+## Configuration
+
+Alternatively, you can configure the Slack application with a project's
+integration settings.
+
+Keep in mind that you need to have the appropriate permissions for your Slack
+team in order to be able to install a new application, read more in Slack's
+docs on [Adding an app to your team][slack-docs].
+
+To enable GitLab's service for your Slack team:
+
+1. Go to your project's **Settings > Integration > Slack application** (only
+ visible on GitLab.com)
+1. Click the "Add to Slack" button
+
+That's all! You can now start using the Slack slash commands.
+
+## Usage
+
+After confirming the installation, you, and everyone else in your Slack team,
+can use all the [slash commands].
+
+When you perform your first slash command you will be asked to authorize your
+Slack user on GitLab.com.
+
+The only difference with the [manually configurable Slack slash commands][slack-manual]
+is that all the commands should be prefixed with the `/gitlab` keyword.
+We are working on making this configurable in the future.
+
+For example, to show the issue number `1001` under the `gitlab-org/gitlab-ce`
+project, you would do:
+
+```
+/gitlab gitlab-org/gitlab-ce issue show 1001
+```
+
+[slack-docs]: https://get.slack.help/hc/en-us/articles/202035138-Adding-apps-to-your-team
+[slash commands]: ../../../integration/slash_commands.md
+[slack-manual]: slack_slash_commands.md
diff --git a/doc/user/project/integrations/img/gitlab_slack_app_landing_page.png b/doc/user/project/integrations/img/gitlab_slack_app_landing_page.png
new file mode 100644
index 00000000000..57cd35c9f5d
--- /dev/null
+++ b/doc/user/project/integrations/img/gitlab_slack_app_landing_page.png
Binary files differ
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index 42c7824a125..339e6873c41 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -14,8 +14,7 @@ want to configure.
![Project services list](img/project_services.png)
-Below, you will find a list of the currently supported ones accompanied with
-comprehensive documentation.
+Below, you will find a list of the currently supported ones accompanied with comprehensive documentation.
## Services
@@ -46,7 +45,8 @@ Click on the service links to see further configuration instructions and details
| Packagist | Update your project on Packagist, the main Composer repository |
| Pipelines emails | Email the pipeline status to a list of recipients |
| [Slack Notifications](slack.md) | Send GitLab events (e.g. issue created) to Slack as notifications |
-| [Slack slash commands](slack_slash_commands.md) | Use slash commands in Slack to control GitLab |
+| [Slack slash commands](slack_slash_commands.md) **[CORE ONLY]** | Use slash commands in Slack to control GitLab |
+| [GitLab Slack application](gitlab_slack_application.md) **[FREE ONLY]** | Use Slack's official application |
| PivotalTracker | Project Management Software (Source Commits Endpoint) |
| [Prometheus](prometheus.md) | Monitor the performance of your deployed apps |
| Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop |
diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md
index c267da69bb3..371e78ca3a4 100644
--- a/doc/user/project/integrations/slack_slash_commands.md
+++ b/doc/user/project/integrations/slack_slash_commands.md
@@ -1,10 +1,15 @@
-# Slack slash commands
+# Slack slash commands **[CORE ONLY]**
-> Introduced in GitLab 8.15
+> Introduced in GitLab 8.15.
-Slack slash commands allow you to control GitLab and view content right inside Slack, without having to leave it. This requires configurations in both Slack and GitLab.
+Slack slash commands allow you to control GitLab and view content right inside
+Slack, without having to leave it. This requires configurations in both Slack and GitLab.
-> Note: GitLab can also send events (e.g. issue created) to Slack as notifications. This is the separately configured [Slack Notifications Service](slack.md).
+GitLab can also send events (e.g., `issue created`) to Slack as notifications.
+This is the separately configured [Slack Notifications Service](slack.md).
+
+NOTE: **Note:**
+For GitLab.com, use the [Slack app](gitlab_slack_application.md) instead.
## Configuration
diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md
index a3f40c20192..629b5e1fde4 100644
--- a/doc/user/project/pipelines/job_artifacts.md
+++ b/doc/user/project/pipelines/job_artifacts.md
@@ -55,7 +55,8 @@ For more examples on artifacts, follow the [artifacts reference in
> **Note:**
> With [GitLab 10.1][ce-14399], HTML files in a public project can be previewed
> directly in a new tab without the need to download them when
-> [GitLab Pages](../../../administration/pages/index.md) is enabled
+> [GitLab Pages](../../../administration/pages/index.md) is enabled.
+> The same holds for textual formats (currently supported extensions: `.txt`, `.json`, and `.log`).
After a job finishes, if you visit the job's specific page, there are three
buttons. You can download the artifacts archive or browse its contents, whereas
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 718566a539f..97ecc4c0d65 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -97,7 +97,7 @@ Some things to note about precedence:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2508) in GitLab 9.1
-[Jupyter][jupyter] Notebook (previously IPython Notebook) files are used for
+[Jupyter](https://jupyter.org) Notebook (previously IPython Notebook) files are used for
interactive computing in many fields and contain a complete record of the
user's sessions and include code, narrative text, equations and rich output.
@@ -220,14 +220,10 @@ Select branches to compare using the [branch filter search box](branches/index.m
Find it under your project's **Repository > Compare**.
-## Locked files
+## Locked files **[PREMIUM]**
-> Available in [GitLab Premium](https://about.gitlab.com/pricing/).
-
-Lock your files to prevent any conflicting changes.
-
-[File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) is available only in
-[GitLab Premium](https://about.gitlab.com/pricing/).
+Use [File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) to
+lock your files to prevent any conflicting changes.
## Repository's API
@@ -243,22 +239,19 @@ used for cloning your project. The button is only shown on macOS.
## Download Source Code
-Source code stored in the repository can be downloaded.
+> Support for directory download was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/24704) in GitLab 11.11.
+The source code stored in a repository can be downloaded from the UI.
By clicking the download icon, a dropdown will open with links to download the following:
![Download source code](img/download_source_code.png)
-- **Source Code:**
- This allows users to download the source code on branch they're currently
- viewing. Available zip, tar, tar.gz and tar.bz2.
+- **Source code:**
+ allows users to download the source code on branch they're currently
+ viewing. Available extensions: `zip`, `tar`, `tar.gz`, and `tar.bz2`.
- **Directory:**
- > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/24704) in GitLab 11.10
-
- Only shows up when viewing a sub-directory. This allows users to download
- the specific directory they're currently viewing. Also available in zip, tar,
- tar.gz and tar.bz2.
+ only shows up when viewing a sub-directory. This allows users to download
+ the specific directory they're currently viewing. Also available in `zip`,
+ `tar`, `tar.gz`, and `tar.bz2`.
- **Artifacts:**
- This allows users to download the artifacts of the latest CI build.
-
-[jupyter]: https://jupyter.org
+ allows users to download the artifacts of the latest CI build.
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 5c98b0ad56c..65d7f68bbf9 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -162,8 +162,8 @@ module API
result = DeleteBranchService.new(user_project, current_user)
.execute(params[:branch])
- if result[:status] != :success
- render_api_error!(result[:message], result[:return_code])
+ if result.error?
+ render_api_error!(result.message, result.http_status)
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 8a21d44b4bf..7e4539d0419 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -67,10 +67,6 @@ module API
initial_current_user != current_user
end
- def user_namespace
- @user_namespace ||= find_namespace!(params[:id])
- end
-
def user_group
@group ||= find_group!(params[:id])
end
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index 3cc09f6ac3f..77ecb3e7cde 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -44,6 +44,8 @@ module API
requires :id, type: String, desc: "Namespace's ID or path"
end
get ':id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ user_namespace = find_namespace!(params[:id])
+
present user_namespace, with: Entities::Namespace, current_user: current_user
end
end
diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb
index 994074ddc67..5724adb2c40 100644
--- a/lib/api/wikis.rb
+++ b/lib/api/wikis.rb
@@ -33,7 +33,8 @@ module API
authorize! :read_wiki, user_project
entity = params[:with_content] ? Entities::WikiPage : Entities::WikiPageBasic
- present user_project.wiki.pages, with: entity
+
+ present user_project.wiki.list_pages(load_content: params[:with_content]), with: entity
end
desc 'Get a wiki page' do
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index 77e4c438bd0..4d67140b0a1 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -82,16 +82,18 @@ module Banzai
def object_link_text(object, matches)
label_suffix = ''
+ parent = project || group
if project || full_path_ref?(matches)
project_path = full_project_path(matches[:namespace], matches[:project])
parent_from_ref = from_ref_cached(project_path)
- reference = parent_from_ref.to_human_reference(project || group)
+ reference = parent_from_ref.to_human_reference(parent)
label_suffix = " <i>in #{reference}</i>" if reference.present?
end
- LabelsHelper.render_colored_label(object, label_suffix: label_suffix, title: tooltip_title(object))
+ presenter = object.present(issuable_subject: parent)
+ LabelsHelper.render_colored_label(presenter, label_suffix: label_suffix, title: tooltip_title(presenter))
end
def tooltip_title(label)
diff --git a/lib/gitlab/checks/branch_check.rb b/lib/gitlab/checks/branch_check.rb
index 1dbd564fb6f..4ddc1c718c7 100644
--- a/lib/gitlab/checks/branch_check.rb
+++ b/lib/gitlab/checks/branch_check.rb
@@ -48,7 +48,7 @@ module Gitlab
if project.empty_repo?
protected_branch_push_checks
- elsif creation? && protected_branch_creation_enabled?
+ elsif creation?
protected_branch_creation_checks
elsif deletion?
protected_branch_deletion_checks
@@ -124,10 +124,6 @@ module Gitlab
Gitlab::Routing.url_helpers.project_project_members_url(project)
end
- def protected_branch_creation_enabled?
- Feature.enabled?(:protected_branch_creation, project, default_enabled: true)
- end
-
def matching_merge_request?
Checks::MatchingMergeRequest.new(newrev, branch_name, project).match?
end
diff --git a/lib/gitlab/checks/lfs_check.rb b/lib/gitlab/checks/lfs_check.rb
index cc6a14d2d9a..67a65d61441 100644
--- a/lib/gitlab/checks/lfs_check.rb
+++ b/lib/gitlab/checks/lfs_check.rb
@@ -7,6 +7,7 @@ module Gitlab
ERROR_MESSAGE = 'LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".'.freeze
def validate!
+ return unless Feature.enabled?(:lfs_check, default_enabled: true)
return unless project.lfs_enabled?
return if skip_lfs_integrity_check
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb b/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb
new file mode 100644
index 00000000000..5fcc9406cc8
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Expression
+ module Lexeme
+ class NotEquals < Lexeme::Operator
+ PATTERN = /!=/.freeze
+
+ def initialize(left, right)
+ @left = left
+ @right = right
+ end
+
+ def evaluate(variables = {})
+ @left.evaluate(variables) != @right.evaluate(variables)
+ end
+
+ def self.build(_value, behind, ahead)
+ new(behind, ahead)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb b/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb
new file mode 100644
index 00000000000..14544d33e25
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Expression
+ module Lexeme
+ class NotMatches < Lexeme::Operator
+ PATTERN = /\!~/.freeze
+
+ def initialize(left, right)
+ @left = left
+ @right = right
+ end
+
+ def evaluate(variables = {})
+ text = @left.evaluate(variables)
+ regexp = @right.evaluate(variables)
+
+ regexp.scan(text.to_s).none?
+ end
+
+ def self.build(_value, behind, ahead)
+ new(behind, ahead)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/expression/lexer.rb b/lib/gitlab/ci/pipeline/expression/lexer.rb
index f26542361a2..e14edfae51d 100644
--- a/lib/gitlab/ci/pipeline/expression/lexer.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexer.rb
@@ -15,7 +15,9 @@ module Gitlab
Expression::Lexeme::Pattern,
Expression::Lexeme::Null,
Expression::Lexeme::Equals,
- Expression::Lexeme::Matches
+ Expression::Lexeme::Matches,
+ Expression::Lexeme::NotEquals,
+ Expression::Lexeme::NotMatches
].freeze
MAX_TOKENS = 100
diff --git a/lib/gitlab/ci/pipeline/expression/statement.rb b/lib/gitlab/ci/pipeline/expression/statement.rb
index b03611f756e..ab5ae9caeea 100644
--- a/lib/gitlab/ci/pipeline/expression/statement.rb
+++ b/lib/gitlab/ci/pipeline/expression/statement.rb
@@ -8,13 +8,24 @@ module Gitlab
StatementError = Class.new(Expression::ExpressionError)
GRAMMAR = [
+ # presence matchers
%w[variable],
+
+ # positive matchers
%w[variable equals string],
%w[variable equals variable],
%w[variable equals null],
%w[string equals variable],
%w[null equals variable],
- %w[variable matches pattern]
+ %w[variable matches pattern],
+
+ # negative matchers
+ %w[variable notequals string],
+ %w[variable notequals variable],
+ %w[variable notequals null],
+ %w[string notequals variable],
+ %w[null notequals variable],
+ %w[variable notmatches pattern]
].freeze
def initialize(statement, variables = {})
diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
index d36576fe39f..9d99d04d263 100644
--- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
@@ -490,7 +490,7 @@ rollout 100%:
fi
helm init --client-only
- helm repo add gitlab ${AUTO_DEVOPS_CHART_REPOSITORY:-https://charts.gitlab.io}
+ helm repo add ${AUTO_DEVOPS_CHART_REPOSITORY_NAME:-gitlab} ${AUTO_DEVOPS_CHART_REPOSITORY:-https://charts.gitlab.io} ${AUTO_DEVOPS_CHART_REPOSITORY_USERNAME:+"--username" "$AUTO_DEVOPS_CHART_REPOSITORY_USERNAME"} ${AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD:+"--password" "$AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD"}
if [[ ! -d "$auto_chart" ]]; then
helm fetch ${auto_chart} --untar
fi
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index a22e3c4b9dd..c12cb6a6434 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -732,18 +732,29 @@ module Gitlab
end
def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:)
+ reachable_ref =
+ if source_repository == self
+ source_branch_name
+ else
+ # If a tmp ref was created before for a separate repo comparison (forks),
+ # we're able to short-circuit the tmp ref re-creation:
+ # 1. Take the SHA from the source repo
+ # 2. Read that in the current "target" repo
+ # 3. If that SHA is still known (readable), it means GC hasn't
+ # cleaned it up yet, so we can use it instead re-writing the tmp ref.
+ source_commit_id = source_repository.commit(source_branch_name)&.sha
+ commit(source_commit_id)&.sha if source_commit_id
+ end
+
+ return compare(target_branch_name, reachable_ref, straight: straight) if reachable_ref
+
tmp_ref = "refs/tmp/#{SecureRandom.hex}"
return unless fetch_source_branch!(source_repository, source_branch_name, tmp_ref)
- Gitlab::Git::Compare.new(
- self,
- target_branch_name,
- tmp_ref,
- straight: straight
- )
+ compare(target_branch_name, tmp_ref, straight: straight)
ensure
- delete_refs(tmp_ref)
+ delete_refs(tmp_ref) if tmp_ref
end
def write_ref(ref_path, ref, old_ref: nil)
@@ -999,6 +1010,13 @@ module Gitlab
private
+ def compare(base_ref, head_ref, straight:)
+ Gitlab::Git::Compare.new(self,
+ base_ref,
+ head_ref,
+ straight: straight)
+ end
+
def empty_diff_stats
Gitlab::Git::DiffStatsCollection.new([])
end
diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb
index a0dd4a24363..c1bcd8e934a 100644
--- a/lib/gitlab/git/wiki.rb
+++ b/lib/gitlab/git/wiki.rb
@@ -86,9 +86,14 @@ module Gitlab
end
end
- def pages(limit: 0, sort: nil, direction_desc: false)
+ def list_pages(limit: 0, sort: nil, direction_desc: false, load_content: false)
wrapped_gitaly_errors do
- gitaly_get_all_pages(limit: limit, sort: sort, direction_desc: direction_desc)
+ gitaly_list_pages(
+ limit: limit,
+ sort: sort,
+ direction_desc: direction_desc,
+ load_content: load_content
+ )
end
end
@@ -168,10 +173,17 @@ module Gitlab
Gitlab::Git::WikiFile.new(wiki_file)
end
- def gitaly_get_all_pages(limit: 0, sort: nil, direction_desc: false)
- gitaly_wiki_client.get_all_pages(
- limit: limit, sort: sort, direction_desc: direction_desc
- ).map do |wiki_page, version|
+ def gitaly_list_pages(limit: 0, sort: nil, direction_desc: false, load_content: false)
+ params = { limit: limit, sort: sort, direction_desc: direction_desc }
+
+ gitaly_pages =
+ if load_content
+ gitaly_wiki_client.load_all_pages(params)
+ else
+ gitaly_wiki_client.list_all_pages(params)
+ end
+
+ gitaly_pages.map do |wiki_page, version|
Gitlab::Git::WikiPage.new(wiki_page, version)
end
end
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index 6b8e58e6199..8ccefb00d20 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -55,13 +55,13 @@ module Gitlab
def get_blobs(revision_paths, limit = -1)
return [] if revision_paths.empty?
- revision_paths.map! do |rev, path|
+ request_revision_paths = revision_paths.map do |rev, path|
Gitaly::GetBlobsRequest::RevisionPath.new(revision: rev, path: encode_binary(path))
end
request = Gitaly::GetBlobsRequest.new(
repository: @gitaly_repo,
- revision_paths: revision_paths,
+ revision_paths: request_revision_paths,
limit: limit
)
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 0d5debfcd01..2896b7e1ce0 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -174,7 +174,7 @@ module Gitlab
response.each_with_object({}) do |gitaly_response, hsh|
gitaly_response.commits.each do |commit_for_tree|
- hsh[commit_for_tree.path] = Gitlab::Git::Commit.new(@repository, commit_for_tree.commit)
+ hsh[commit_for_tree.path_bytes] = Gitlab::Git::Commit.new(@repository, commit_for_tree.commit)
end
end
end
diff --git a/lib/gitlab/gitaly_client/wiki_service.rb b/lib/gitlab/gitaly_client/wiki_service.rb
index e036cdcd800..ce9faad825c 100644
--- a/lib/gitlab/gitaly_client/wiki_service.rb
+++ b/lib/gitlab/gitaly_client/wiki_service.rb
@@ -87,7 +87,27 @@ module Gitlab
wiki_page_from_iterator(response)
end
- def get_all_pages(limit: 0, sort: nil, direction_desc: false)
+ def list_all_pages(limit: 0, sort: nil, direction_desc: false)
+ sort_value = Gitaly::WikiListPagesRequest::SortBy.resolve(sort.to_s.upcase.to_sym)
+
+ params = { repository: @gitaly_repo, limit: limit, direction_desc: direction_desc }
+ params[:sort] = sort_value if sort_value
+
+ request = Gitaly::WikiListPagesRequest.new(params)
+ stream = GitalyClient.call(@repository.storage, :wiki_service, :wiki_list_pages, request, timeout: GitalyClient.medium_timeout)
+ stream.each_with_object([]) do |message, pages|
+ page = message.page
+
+ next unless page
+
+ wiki_page = GitalyClient::WikiPage.new(page.to_h)
+ version = new_wiki_page_version(page.version)
+
+ pages << [wiki_page, version]
+ end
+ end
+
+ def load_all_pages(limit: 0, sort: nil, direction_desc: false)
sort_value = Gitaly::WikiGetAllPagesRequest::SortBy.resolve(sort.to_s.upcase.to_sym)
params = { repository: @gitaly_repo, limit: limit, direction_desc: direction_desc }
@@ -95,6 +115,7 @@ module Gitlab
request = Gitaly::WikiGetAllPagesRequest.new(params)
response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_all_pages, request, timeout: GitalyClient.medium_timeout)
+
pages = []
loop do
diff --git a/lib/gitlab/graphql/authorize/authorize_field_service.rb b/lib/gitlab/graphql/authorize/authorize_field_service.rb
index 03d6aabb0e3..619ce100421 100644
--- a/lib/gitlab/graphql/authorize/authorize_field_service.rb
+++ b/lib/gitlab/graphql/authorize/authorize_field_service.rb
@@ -48,7 +48,7 @@ module Gitlab
end
def authorize_against(parent_typed_object, resolved_type)
- if built_in_type?
+ if scalar_type?
# The field is a built-in/scalar type, or a list of scalars
# authorize using the parent's object
parent_typed_object.object
@@ -108,8 +108,8 @@ module Gitlab
type.unwrap
end
- def built_in_type?
- GraphQL::Schema::BUILT_IN_TYPES.has_value?(node_type_for_basic_connection(@field.type))
+ def scalar_type?
+ node_type_for_basic_connection(@field.type).kind.scalar?
end
end
end
diff --git a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
index fcf6a25ab00..acb7f225b17 100644
--- a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
@@ -30,10 +30,7 @@ module Gitlab
def handle_response_error(response)
unless response.success?
- error_code = response.dig('Error', 'Code') || response.code
- error_message = response.dig('Error', 'Message') || response.message
-
- raise StrategyError.new("Error uploading the project. Code #{error_code}: #{error_message}")
+ raise StrategyError.new("Error uploading the project. Code #{response.code}: #{response.message}")
end
end
diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb
index ac392432427..16c16aa0265 100644
--- a/lib/gitlab/performance_bar/peek_query_tracker.rb
+++ b/lib/gitlab/performance_bar/peek_query_tracker.rb
@@ -26,11 +26,7 @@ module Gitlab
subscribe('sql.active_record') do |_, start, finish, _, data|
if Gitlab::SafeRequestStore.store[:peek_enabled]
- # data[:cached] is only available starting from Rails 5.1.0
- # https://github.com/rails/rails/blob/v5.1.0/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L113
- # Before that, data[:name] was set to 'CACHE'
- # https://github.com/rails/rails/blob/v4.2.9/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L80
- unless data.fetch(:cached, data[:name] == 'CACHE')
+ unless data[:cached]
track_query(data[:sql].strip, data[:binds], start, finish)
end
end
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 8988b9ad7be..a29517e068f 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -113,7 +113,7 @@ module Gitlab
issues.full_search(query)
end
- issues.reorder('updated_at DESC')
+ issues.reorder('issues.updated_at DESC')
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -121,7 +121,7 @@ module Gitlab
def milestones
milestones = Milestone.where(project_id: project_ids_relation)
milestones = milestones.search(query)
- milestones.reorder('updated_at DESC')
+ milestones.reorder('milestones.updated_at DESC')
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -139,7 +139,7 @@ module Gitlab
merge_requests.full_search(query)
end
- merge_requests.reorder('updated_at DESC')
+ merge_requests.reorder('merge_requests.updated_at DESC')
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/quality/seeders/issues.rb b/lib/quality/seeders/issues.rb
new file mode 100644
index 00000000000..4c8cb6e97cc
--- /dev/null
+++ b/lib/quality/seeders/issues.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+# rubocop:disable CodeReuse/ActiveRecord
+module Quality
+ module Seeders
+ class Issues
+ DEFAULT_BACKFILL_WEEKS = 52
+ DEFAULT_AVERAGE_ISSUES_PER_WEEK = 10
+
+ attr_reader :project, :user
+
+ def initialize(project:)
+ @project = project
+ end
+
+ def seed(backfill_weeks: DEFAULT_BACKFILL_WEEKS, average_issues_per_week: DEFAULT_AVERAGE_ISSUES_PER_WEEK)
+ created_at = backfill_weeks.to_i.weeks.ago
+ team = project.team.users
+ created_issues_count = 0
+
+ loop do
+ rand(average_issues_per_week * 2).times do
+ params = {
+ title: FFaker::Lorem.sentence(6),
+ description: FFaker::Lorem.sentence,
+ created_at: created_at + rand(6).days,
+ state: %w[opened closed].sample,
+ milestone: project.milestones.sample,
+ assignee_ids: Array(team.pluck(:id).sample(3)),
+ labels: labels.join(',')
+ }
+ issue = ::Issues::CreateService.new(project, team.sample, params).execute
+
+ if issue.persisted?
+ created_issues_count += 1
+ print '.' # rubocop:disable Rails/Output
+ end
+ end
+
+ created_at += 1.week
+
+ break if created_at > Time.now
+ end
+
+ created_issues_count
+ end
+
+ private
+
+ def labels
+ @labels_pool ||= project.labels.limit(rand(3)).pluck(:title).tap do |labels_array|
+ labels_array.concat(project.group.labels.limit(rand(3)).pluck(:title)) if project.group
+ end
+ end
+ end
+ end
+end
+# rubocop:enable CodeReuse/ActiveRecord
diff --git a/lib/tasks/gitlab/seed.rake b/lib/tasks/gitlab/seed.rake
new file mode 100644
index 00000000000..155ba979b36
--- /dev/null
+++ b/lib/tasks/gitlab/seed.rake
@@ -0,0 +1,34 @@
+namespace :gitlab do
+ namespace :seed do
+ desc "GitLab | Seed | Seeds issues"
+ task :issues, [:project_full_path] => :environment do |t, args|
+ projects =
+ if args.project_full_path
+ project = Project.find_by_full_path(args.project_full_path)
+
+ unless project
+ error_message = "Project '#{args.project_full_path}' does not exist!"
+ potential_projects = Project.search(args.project_full_path)
+
+ if potential_projects.present?
+ error_message += " Did you mean '#{potential_projects.first.full_path}'?"
+ end
+
+ puts error_message.color(:red)
+ exit 1
+ end
+
+ [project]
+ else
+ Project.find_each
+ end
+
+ projects.each do |project|
+ puts "\nSeeding issues for the '#{project.full_path}' project"
+ seeder = Quality::Seeders::Issues.new(project: project)
+ issues_created = seeder.seed(backfill_weeks: 5, average_issues_per_week: 2)
+ puts "\n#{issues_created} issues created!"
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 54c40e48084..7447139566b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -372,6 +372,9 @@ msgstr ""
msgid "A deleted user"
msgstr ""
+msgid "A fork is a copy of a project.<br />Forking a repository allows you to make changes without affecting the original project."
+msgstr ""
+
msgid "A member of GitLab's abuse team will review your report as soon as possible."
msgstr ""
@@ -1887,6 +1890,9 @@ msgstr ""
msgid "Close milestone"
msgstr ""
+msgid "Close sidebar"
+msgstr ""
+
msgid "Closed"
msgstr ""
@@ -3256,6 +3262,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -4140,6 +4149,15 @@ msgstr ""
msgid "For public projects, anyone can view pipelines and access job details (output logs and artifacts)"
msgstr ""
+msgid "Fork"
+msgstr ""
+
+msgid "Fork Error!"
+msgstr ""
+
+msgid "Fork project"
+msgstr ""
+
msgid "ForkedFromProjectPath|Forked from"
msgstr ""
@@ -4149,6 +4167,9 @@ msgstr ""
msgid "Forking in progress"
msgstr ""
+msgid "Forking repository"
+msgstr ""
+
msgid "Forks"
msgstr ""
@@ -4311,6 +4332,9 @@ msgstr ""
msgid "Go to project"
msgstr ""
+msgid "Go to your fork"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -5775,6 +5799,9 @@ msgstr ""
msgid "Name"
msgstr ""
+msgid "Name has already been taken"
+msgstr ""
+
msgid "Name new label"
msgstr ""
@@ -5927,6 +5954,9 @@ msgstr ""
msgid "No activities found"
msgstr ""
+msgid "No available namespaces to fork the project."
+msgstr ""
+
msgid "No branches found"
msgstr ""
@@ -7835,6 +7865,9 @@ msgstr ""
msgid "Search for projects, issues, etc."
msgstr ""
+msgid "Search forks"
+msgstr ""
+
msgid "Search groups"
msgstr ""
@@ -9709,6 +9742,9 @@ msgstr ""
msgid "Try again?"
msgstr ""
+msgid "Try to fork again"
+msgstr ""
+
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
@@ -10533,6 +10569,9 @@ msgstr ""
msgid "You must have maintainer access to force delete a lock"
msgstr ""
+msgid "You must have permission to create a project in a namespace before forking."
+msgstr ""
+
msgid "You need permission."
msgstr ""
@@ -10548,6 +10587,9 @@ msgstr ""
msgid "You need to upload a Google Takeout archive."
msgstr ""
+msgid "You tried to fork %{link_to_the_project} but it failed for the following reason:"
+msgstr ""
+
msgid "You will lose all changes you've made to this file. This action cannot be undone."
msgstr ""
@@ -11199,6 +11241,9 @@ msgstr ""
msgid "sign in"
msgstr ""
+msgid "sort:"
+msgstr ""
+
msgid "source"
msgstr ""
diff --git a/package.json b/package.json
index 1cb9f7a9ade..d2ee29fe06d 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"@babel/preset-env": "^7.3.1",
"@gitlab/csslab": "^1.9.0",
"@gitlab/svgs": "^1.59.0",
- "@gitlab/ui": "^3.3.0",
+ "@gitlab/ui": "^3.5.0",
"apollo-cache-inmemory": "^1.5.1",
"apollo-client": "^2.5.1",
"apollo-upload-client": "^10.0.0",
diff --git a/qa/Gemfile b/qa/Gemfile
index 38e95ba2d65..64215b24cf1 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -7,6 +7,6 @@ gem 'rake', '~> 12.3.0'
gem 'rspec', '~> 3.7'
gem 'selenium-webdriver', '~> 3.12'
gem 'airborne', '~> 0.2.13'
-gem 'nokogiri', '~> 1.10.1'
+gem 'nokogiri', '~> 1.10.3'
gem 'rspec-retry', '~> 0.6.1'
gem 'faker', '~> 1.6', '>= 1.6.6'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 9d3d42fb6ae..a06c88b6f0a 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -49,7 +49,7 @@ GEM
mini_portile2 (2.4.0)
minitest (5.11.1)
netrc (0.11.0)
- nokogiri (1.10.2)
+ nokogiri (1.10.3)
mini_portile2 (~> 2.4.0)
pry (0.11.3)
coderay (~> 1.1.0)
@@ -102,7 +102,7 @@ DEPENDENCIES
capybara (~> 2.16.1)
capybara-screenshot (~> 1.0.18)
faker (~> 1.6, >= 1.6.6)
- nokogiri (~> 1.10.1)
+ nokogiri (~> 1.10.3)
pry-byebug (~> 3.5.1)
rake (~> 12.3.0)
rspec (~> 3.7)
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index b55ce1af55e..8be22dc0278 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -1,26 +1,6 @@
[[ "$TRACE" ]] && set -x
export TILLER_NAMESPACE="$KUBE_NAMESPACE"
-function echoerr() {
- local header="${2}"
-
- if [ -n "${header}" ]; then
- printf "\n\033[0;31m** %s **\n\033[0m" "${1}" >&2;
- else
- printf "\033[0;31m%s\n\033[0m" "${1}" >&2;
- fi
-}
-
-function echoinfo() {
- local header="${2}"
-
- if [ -n "${header}" ]; then
- printf "\n\033[0;33m** %s **\n\033[0m" "${1}" >&2;
- else
- printf "\033[0;33m%s\n\033[0m" "${1}" >&2;
- fi
-}
-
function deployExists() {
local namespace="${1}"
local deploy="${2}"
@@ -328,80 +308,3 @@ function add_license() {
puts "License added";
'
}
-
-function get_job_id() {
- local job_name="${1}"
- local query_string="${2:+&${2}}"
-
- local max_page=3
- local page=1
-
- while true; do
- local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/jobs?per_page=100&page=${page}${query_string}"
- echoinfo "GET ${url}"
-
- local job_id
- job_id=$(curl --silent --show-error --header "PRIVATE-TOKEN: ${API_TOKEN}" "${url}" | jq "map(select(.name == \"${job_name}\")) | map(.id) | last")
- [[ "${job_id}" == "null" && "${page}" -lt "$max_page" ]] || break
-
- let "page++"
- done
-
- if [[ "${job_id}" == "" ]]; then
- echoerr "The '${job_name}' job ID couldn't be retrieved!"
- else
- echoinfo "The '${job_name}' job ID is ${job_id}"
- echo "${job_id}"
- fi
-}
-
-function play_job() {
- local job_name="${1}"
- local job_id
- job_id=$(get_job_id "${job_name}" "scope=manual");
- if [ -z "${job_id}" ]; then return; fi
-
- local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/jobs/${job_id}/play"
- echoinfo "POST ${url}"
-
- local job_url
- job_url=$(curl --silent --show-error --request POST --header "PRIVATE-TOKEN: ${API_TOKEN}" "${url}" | jq ".web_url")
- echoinfo "Manual job '${job_name}' started at: ${job_url}"
-}
-
-function wait_for_job_to_be_done() {
- local job_name="${1}"
- local query_string="${2}"
- local job_id
- job_id=$(get_job_id "${job_name}" "${query_string}")
- if [ -z "${job_id}" ]; then return; fi
-
- echoinfo "Waiting for the '${job_name}' job to finish..."
-
- local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/jobs/${job_id}"
- echoinfo "GET ${url}"
-
- # In case the job hasn't finished yet. Keep trying until the job times out.
- local interval=30
- local elapsed_seconds=0
- while true; do
- local job_status
- job_status=$(curl --silent --show-error --header "PRIVATE-TOKEN: ${API_TOKEN}" "${url}" | jq ".status" | sed -e s/\"//g)
- [[ "${job_status}" == "pending" || "${job_status}" == "running" ]] || break
-
- printf "."
- let "elapsed_seconds+=interval"
- sleep ${interval}
- done
-
- local elapsed_minutes=$((elapsed_seconds / 60))
- echoinfo "Waited '${job_name}' for ${elapsed_minutes} minutes."
-
- if [[ "${job_status}" == "failed" ]]; then
- echoerr "The '${job_name}' failed."
- elif [[ "${job_status}" == "manual" ]]; then
- echoinfo "The '${job_name}' is manual."
- else
- echoinfo "The '${job_name}' passed."
- fi
-}
diff --git a/scripts/utils.sh b/scripts/utils.sh
index 2d2ba115563..4a6567b8a62 100644
--- a/scripts/utils.sh
+++ b/scripts/utils.sh
@@ -1,4 +1,4 @@
-retry() {
+function retry() {
if eval "$@"; then
return 0
fi
@@ -13,15 +13,15 @@ retry() {
return 1
}
-setup_db_user_only() {
+function setup_db_user_only() {
if [ "$GITLAB_DATABASE" = "postgresql" ]; then
- . scripts/create_postgres_user.sh
+ source scripts/create_postgres_user.sh
else
- . scripts/create_mysql_user.sh
+ source scripts/create_mysql_user.sh
fi
}
-setup_db() {
+function setup_db() {
setup_db_user_only
bundle exec rake db:drop db:create db:schema:load db:migrate
@@ -30,3 +30,129 @@ setup_db() {
bundle exec rake add_limits_mysql
fi
}
+
+function install_api_client_dependencies_with_apk() {
+ apk add --update openssl curl jq
+}
+
+function install_api_client_dependencies_with_apt() {
+ apt update && apt install jq -y
+}
+
+function install_gitlab_gem() {
+ gem install gitlab --no-document
+}
+
+function echoerr() {
+ local header="${2}"
+
+ if [ -n "${header}" ]; then
+ printf "\n\033[0;31m** %s **\n\033[0m" "${1}" >&2;
+ else
+ printf "\033[0;31m%s\n\033[0m" "${1}" >&2;
+ fi
+}
+
+function echoinfo() {
+ local header="${2}"
+
+ if [ -n "${header}" ]; then
+ printf "\n\033[0;33m** %s **\n\033[0m" "${1}" >&2;
+ else
+ printf "\033[0;33m%s\n\033[0m" "${1}" >&2;
+ fi
+}
+
+function get_job_id() {
+ local job_name="${1}"
+ local query_string="${2:+&${2}}"
+ local api_token="${API_TOKEN-${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}}"
+ if [ -z "${api_token}" ]; then
+ echoerr "Please provide an API token with \$API_TOKEN or \$GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN."
+ return
+ fi
+
+ local max_page=3
+ local page=1
+
+ while true; do
+ local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/jobs?per_page=100&page=${page}${query_string}"
+ echoinfo "GET ${url}"
+
+ local job_id
+ job_id=$(curl --silent --show-error --header "PRIVATE-TOKEN: ${api_token}" "${url}" | jq "map(select(.name == \"${job_name}\")) | map(.id) | last")
+ [[ "${job_id}" == "null" && "${page}" -lt "$max_page" ]] || break
+
+ let "page++"
+ done
+
+ if [[ "${job_id}" == "" ]]; then
+ echoerr "The '${job_name}' job ID couldn't be retrieved!"
+ else
+ echoinfo "The '${job_name}' job ID is ${job_id}"
+ echo "${job_id}"
+ fi
+}
+
+function play_job() {
+ local job_name="${1}"
+ local job_id
+ job_id=$(get_job_id "${job_name}" "scope=manual");
+ if [ -z "${job_id}" ]; then return; fi
+
+ local api_token="${API_TOKEN-${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}}"
+ if [ -z "${api_token}" ]; then
+ echoerr "Please provide an API token with \$API_TOKEN or \$GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN."
+ return
+ fi
+
+ local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/jobs/${job_id}/play"
+ echoinfo "POST ${url}"
+
+ local job_url
+ job_url=$(curl --silent --show-error --request POST --header "PRIVATE-TOKEN: ${api_token}" "${url}" | jq ".web_url")
+ echoinfo "Manual job '${job_name}' started at: ${job_url}"
+}
+
+function wait_for_job_to_be_done() {
+ local job_name="${1}"
+ local query_string="${2}"
+ local job_id
+ job_id=$(get_job_id "${job_name}" "${query_string}")
+ if [ -z "${job_id}" ]; then return; fi
+
+ local api_token="${API_TOKEN-${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}}"
+ if [ -z "${api_token}" ]; then
+ echoerr "Please provide an API token with \$API_TOKEN or \$GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN."
+ return
+ fi
+
+ echoinfo "Waiting for the '${job_name}' job to finish..."
+
+ local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/jobs/${job_id}"
+ echoinfo "GET ${url}"
+
+ # In case the job hasn't finished yet. Keep trying until the job times out.
+ local interval=30
+ local elapsed_seconds=0
+ while true; do
+ local job_status
+ job_status=$(curl --silent --show-error --header "PRIVATE-TOKEN: ${api_token}" "${url}" | jq ".status" | sed -e s/\"//g)
+ [[ "${job_status}" == "pending" || "${job_status}" == "running" ]] || break
+
+ printf "."
+ let "elapsed_seconds+=interval"
+ sleep ${interval}
+ done
+
+ local elapsed_minutes=$((elapsed_seconds / 60))
+ echoinfo "Waited '${job_name}' for ${elapsed_minutes} minutes."
+
+ if [[ "${job_status}" == "failed" ]]; then
+ echoerr "The '${job_name}' failed."
+ elif [[ "${job_status}" == "manual" ]]; then
+ echoinfo "The '${job_name}' is manual."
+ else
+ echoinfo "The '${job_name}' passed."
+ fi
+}
diff --git a/spec/controllers/concerns/issuable_collections_spec.rb b/spec/controllers/concerns/issuable_collections_spec.rb
index f9d15d04719..fb2cd5ca955 100644
--- a/spec/controllers/concerns/issuable_collections_spec.rb
+++ b/spec/controllers/concerns/issuable_collections_spec.rb
@@ -108,51 +108,77 @@ describe IssuableCollections do
end
describe '#finder_options' do
- let(:params) do
- {
- assignee_id: '1',
- assignee_username: 'user1',
- author_id: '2',
- author_username: 'user2',
- authorized_only: 'yes',
- confidential: true,
- due_date: '2017-01-01',
- group_id: '3',
- iids: '4',
- label_name: ['foo'],
- milestone_title: 'bar',
- my_reaction_emoji: 'thumbsup',
- non_archived: 'true',
- project_id: '5',
- scope: 'all',
- search: 'baz',
- sort: 'priority',
- state: 'opened',
- invalid_param: 'invalid_param'
- }
- end
-
- it 'only allows whitelisted params' do
+ before do
allow(controller).to receive(:cookies).and_return({})
allow(controller).to receive(:current_user).and_return(nil)
+ end
+
+ subject { controller.send(:finder_options).to_h }
+
+ context 'scalar params' do
+ let(:params) do
+ {
+ assignee_id: '1',
+ assignee_username: 'user1',
+ author_id: '2',
+ author_username: 'user2',
+ authorized_only: 'yes',
+ confidential: true,
+ due_date: '2017-01-01',
+ group_id: '3',
+ iids: '4',
+ label_name: 'foo',
+ milestone_title: 'bar',
+ my_reaction_emoji: 'thumbsup',
+ non_archived: 'true',
+ project_id: '5',
+ scope: 'all',
+ search: 'baz',
+ sort: 'priority',
+ state: 'opened',
+ invalid_param: 'invalid_param'
+ }
+ end
+
+ it 'only allows whitelisted params' do
+ is_expected.to include({
+ 'assignee_id' => '1',
+ 'assignee_username' => 'user1',
+ 'author_id' => '2',
+ 'author_username' => 'user2',
+ 'confidential' => true,
+ 'label_name' => 'foo',
+ 'milestone_title' => 'bar',
+ 'my_reaction_emoji' => 'thumbsup',
+ 'due_date' => '2017-01-01',
+ 'scope' => 'all',
+ 'search' => 'baz',
+ 'sort' => 'priority',
+ 'state' => 'opened'
+ })
+
+ is_expected.not_to include('invalid_param')
+ end
+ end
+
+ context 'array params' do
+ let(:params) do
+ {
+ assignee_username: %w[user1 user2],
+ label_name: %w[label1 label2],
+ invalid_param: 'invalid_param',
+ invalid_array: ['param']
+ }
+ end
+
+ it 'only allows whitelisted params' do
+ is_expected.to include({
+ 'label_name' => %w[label1 label2],
+ 'assignee_username' => %w[user1 user2]
+ })
- finder_options = controller.send(:finder_options)
-
- expect(finder_options).to eq(ActionController::Parameters.new({
- 'assignee_id' => '1',
- 'assignee_username' => 'user1',
- 'author_id' => '2',
- 'author_username' => 'user2',
- 'confidential' => true,
- 'label_name' => ['foo'],
- 'milestone_title' => 'bar',
- 'my_reaction_emoji' => 'thumbsup',
- 'due_date' => '2017-01-01',
- 'scope' => 'all',
- 'search' => 'baz',
- 'sort' => 'priority',
- 'state' => 'opened'
- }).permit!)
+ is_expected.not_to include('invalid_param', 'invalid_array')
+ end
end
end
end
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 0af0eb4f942..6e374a8daa7 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -9,10 +9,14 @@ describe OmniauthCallbacksController, type: :controller do
let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) }
before do
- mock_auth_hash(provider.to_s, +extern_uid, user.email)
+ @original_env_config_omniauth_auth = mock_auth_hash(provider.to_s, +extern_uid, user.email)
stub_omniauth_provider(provider, context: request)
end
+ after do
+ Rails.application.env_config['omniauth.auth'] = @original_env_config_omniauth_auth
+ end
+
context 'when the user is on the last sign in attempt' do
let(:extern_uid) { 'my-uid' }
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index 1818809518d..92380a2bf09 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -82,7 +82,7 @@ describe Projects::CompareController do
show_request
expect(response).to be_success
- expect(assigns(:diffs).diff_files.to_a).to eq([])
+ expect(assigns(:diffs)).to eq([])
expect(assigns(:commits)).to eq([])
end
end
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index fc9a0adeed2..db53e5bc8a4 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -191,6 +191,15 @@ describe Projects::Settings::CiCdController do
expect(project.build_timeout).to eq(5400)
end
end
+
+ context 'when build_timeout_human_readable is invalid' do
+ let(:params) { { build_timeout_human_readable: '5m' } }
+
+ it 'set specified timeout' do
+ expect(subject).to set_flash[:alert]
+ expect(response).to redirect_to(namespace_project_settings_ci_cd_path)
+ end
+ end
end
end
end
diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb
index e0a6fc52ee9..f2e0b5e5c1d 100644
--- a/spec/controllers/projects/wikis_controller_spec.rb
+++ b/spec/controllers/projects/wikis_controller_spec.rb
@@ -19,6 +19,18 @@ describe Projects::WikisController do
destroy_page(wiki_title)
end
+ describe 'GET #pages' do
+ subject { get :pages, params: { namespace_id: project.namespace, project_id: project, id: wiki_title } }
+
+ it 'does not load the pages content' do
+ expect(controller).to receive(:load_wiki).and_return(project_wiki)
+
+ expect(project_wiki).to receive(:list_pages).twice.and_call_original
+
+ subject
+ end
+ end
+
describe 'GET #show' do
render_views
@@ -28,9 +40,9 @@ describe Projects::WikisController do
expect(controller).to receive(:load_wiki).and_return(project_wiki)
# empty? call
- expect(project_wiki).to receive(:pages).with(limit: 1).and_call_original
+ expect(project_wiki).to receive(:list_pages).with(limit: 1).and_call_original
# Sidebar entries
- expect(project_wiki).to receive(:pages).with(limit: 15).and_call_original
+ expect(project_wiki).to receive(:list_pages).with(limit: 15).and_call_original
subject
@@ -104,7 +116,7 @@ describe Projects::WikisController do
subject
- expect(response).to redirect_to(project_wiki_path(project, project_wiki.pages.first))
+ expect(response).to redirect_to(project_wiki_path(project, project_wiki.list_pages.first))
end
end
@@ -138,7 +150,7 @@ describe Projects::WikisController do
allow(controller).to receive(:valid_encoding?).and_return(false)
subject
- expect(response).to redirect_to(project_wiki_path(project, project_wiki.pages.first))
+ expect(response).to redirect_to(project_wiki_path(project, project_wiki.list_pages.first))
end
end
@@ -148,7 +160,7 @@ describe Projects::WikisController do
it 'updates the page' do
subject
- wiki_page = project_wiki.pages.first
+ wiki_page = project_wiki.list_pages(load_content: true).first
expect(wiki_page.title).to eq new_title
expect(wiki_page.content).to eq new_content
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 9c60f0fcd4d..4634d1d4bb3 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -11,6 +11,30 @@ describe SearchController do
sign_in(user)
end
+ context 'uses the right partials depending on scope' do
+ using RSpec::Parameterized::TableSyntax
+ render_views
+
+ set(:project) { create(:project, :public, :repository, :wiki_repo) }
+
+ subject { get(:show, params: { project_id: project.id, scope: scope, search: 'merge' }) }
+
+ where(:partial, :scope) do
+ '_blob' | :blobs
+ '_wiki_blob' | :wiki_blobs
+ '_commit' | :commits
+ end
+
+ with_them do
+ it do
+ project_wiki = create(:project_wiki, project: project, user: user)
+ create(:wiki_page, wiki: project_wiki, attrs: { title: 'merge', content: 'merge' })
+
+ expect(subject).to render_template("search/results/#{partial}")
+ end
+ end
+ end
+
it 'finds issue comments' do
project = create(:project, :public)
note = create(:note_on_issue, project: project)
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index b1c6f308bc6..29545779a34 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -34,14 +34,11 @@ describe "Admin::Users" do
expect(page).to have_button('Delete user and contributions')
end
- describe "view extra user information", :js do
- it 'does not have the user popover open' do
+ describe "view extra user information" do
+ it 'shows the user popover on hover', :js, :quarantine do
expect(page).not_to have_selector('#__BV_popover_1__')
- end
- it 'shows the user popover on hover' do
first_user_link = page.first('.js-user-link')
-
first_user_link.hover
expect(page).to have_selector('#__BV_popover_1__')
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index fd8677feab5..d58e3b2841e 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -17,6 +17,26 @@ describe 'Dashboard Todos' do
end
end
+ context 'when the todo references a merge request' do
+ let(:referenced_mr) { create(:merge_request, source_project: project) }
+ let(:note) { create(:note, project: project, note: "Check out #{referenced_mr.to_reference}") }
+ let!(:todo) { create(:todo, :mentioned, user: user, project: project, author: author, note: note) }
+
+ before do
+ sign_in(user)
+ visit dashboard_todos_path
+ end
+
+ it 'renders the mr link with the extra attributes' do
+ link = page.find_link(referenced_mr.to_reference)
+
+ expect(link).not_to be_nil
+ expect(link['data-iid']).to eq(referenced_mr.iid.to_s)
+ expect(link['data-project-path']).to eq(referenced_mr.project.full_path)
+ expect(link['data-mr-title']).to eq(referenced_mr.title)
+ end
+ end
+
context 'User has a todo', :js do
before do
create(:todo, :mentioned, user: user, project: project, target: issue, author: author)
diff --git a/spec/features/issuables/markdown_references/jira_spec.rb b/spec/features/issuables/markdown_references/jira_spec.rb
index 8eaccfc0949..cc04798248c 100644
--- a/spec/features/issuables/markdown_references/jira_spec.rb
+++ b/spec/features/issuables/markdown_references/jira_spec.rb
@@ -1,6 +1,6 @@
require "rails_helper"
-describe "Jira", :js do
+describe "Jira", :js, :quarantine do
let(:user) { create(:user) }
let(:actual_project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, target_project: actual_project, source_project: actual_project) }
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index 65de0afae0c..5db54f42264 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -191,29 +191,119 @@ describe 'Merge request > User creates image diff notes', :js do
end
end
- describe 'image view modes' do
- before do
- visit project_commit_path(project, '2f63565e7aac07bcdadb654e253078b727143ec4')
+ shared_examples 'swipe view' do
+ it 'moves the swipe handle' do
+ # Simulate dragging swipe view slider
+ expect { drag_and_drop_by(find('.swipe-bar'), 20, 0) }
+ .to change { find('.swipe-bar')['style'] }
+ .from(a_string_matching('left: 1px'))
end
- it 'resizes image in onion skin view mode' do
- find('.view-modes-menu .onion-skin').click
+ it 'shows both images at the same position' do
+ drag_and_drop_by(find('.swipe-bar'), 40, 0)
- expect(find('.onion-skin-frame')['style']).to match('width: 228px; height: 240px;')
+ expect(left_position('.frame.added img'))
+ .to eq(left_position('.frame.deleted img'))
end
+ end
- it 'resets onion skin view mode opacity when toggling between view modes' do
- find('.view-modes-menu .onion-skin').click
-
+ shared_examples 'onion skin' do
+ it 'resets opacity when toggling between view modes' do
# Simulate dragging onion-skin slider
drag_and_drop_by(find('.dragger'), -30, 0)
expect(find('.onion-skin-frame .frame.added', visible: false)['style']).not_to match('opacity: 1;')
+ switch_to_swipe_view
+ switch_to_onion_skin
+
+ expect(find('.onion-skin-frame .frame.added', visible: false)['style']).to match('opacity: 1;')
+ end
+ end
+
+ describe 'changes tab image diff' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project, target_branch: 'master', source_branch: 'deleted-image-test', author: user) }
+
+ before do
+ visit diffs_project_merge_request_path(project, merge_request)
+ click_link "Changes"
+ end
+
+ def set_image_diff_sources
+ # set path of added and deleted images to something the spec can view
+ page.execute_script("document.querySelector('.frame.added img').src = '/apple-touch-icon.png';")
+ page.execute_script("document.querySelector('.frame.deleted img').src = '/favicon.png';")
+
+ wait_for_requests
+
+ expect(find('.frame.added img', visible: false)['src']).to match('/apple-touch-icon.png')
+ expect(find('.frame.deleted img', visible: false)['src']).to match('/favicon.png')
+ end
+
+ def switch_to_swipe_view
+ # it isn't given the .swipe class in the merge request diff
+ find('.view-modes-menu li:nth-child(2)').click
+ expect(find('.view-modes-menu li.active')).to have_content('Swipe')
+
+ set_image_diff_sources
+ end
+
+ def switch_to_onion_skin
+ # it isn't given the .onion-skin class in the merge request diff
+ find('.view-modes-menu li:nth-child(3)').click
+ expect(find('.view-modes-menu li.active')).to have_content('Onion skin')
+
+ set_image_diff_sources
+ end
+
+ describe 'onion skin' do
+ before do
+ switch_to_onion_skin
+ end
+
+ it_behaves_like 'onion skin'
+ end
+
+ describe 'swipe view' do
+ before do
+ switch_to_swipe_view
+ end
+
+ it_behaves_like 'swipe view'
+ end
+ end
+
+ describe 'image view modes' do
+ before do
+ visit project_commit_path(project, '2f63565e7aac07bcdadb654e253078b727143ec4')
+ end
+
+ def switch_to_swipe_view
find('.view-modes-menu .swipe').click
+ end
+
+ def switch_to_onion_skin
find('.view-modes-menu .onion-skin').click
+ end
- expect(find('.onion-skin-frame .frame.added', visible: false)['style']).to match('opacity: 1;')
+ describe 'onion skin' do
+ before do
+ switch_to_onion_skin
+ end
+
+ it 'resizes image' do
+ expect(find('.onion-skin-frame')['style']).to match('width: 228px; height: 240px;')
+ end
+
+ it_behaves_like 'onion skin'
+ end
+
+ describe 'swipe view' do
+ before do
+ switch_to_swipe_view
+ end
+
+ it_behaves_like 'swipe view'
end
end
@@ -232,4 +322,8 @@ describe 'Merge request > User creates image diff notes', :js do
click_button 'Comment'
wait_for_requests
end
+
+ def left_position(element)
+ page.evaluate_script("document.querySelectorAll('#{element}')[0].getBoundingClientRect().left;")
+ end
end
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 9d5780d29b0..efba303033b 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -137,7 +137,7 @@ describe 'Login' do
enter_code(user.current_otp)
- expect(page).not_to have_content('You are already signed in.')
+ expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated'))
end
context 'using one-time code' do
@@ -317,7 +317,17 @@ describe 'Login' do
gitlab_sign_in(user)
expect(current_path).to eq root_path
- expect(page).not_to have_content('You are already signed in.')
+ expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated'))
+ end
+
+ it 'does not show already signed in message when opening sign in page after login' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+ visit new_user_session_path
+
+ expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated'))
end
end
@@ -579,7 +589,7 @@ describe 'Login' do
click_button 'Accept terms'
expect(current_path).to eq(root_path)
- expect(page).not_to have_content('You are already signed in.')
+ expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated'))
end
it 'does not ask for terms when the user already accepted them' do
diff --git a/spec/fixtures/api/schemas/entities/issue.json b/spec/fixtures/api/schemas/entities/issue.json
index 162fb9c8108..9898819ef75 100644
--- a/spec/fixtures/api/schemas/entities/issue.json
+++ b/spec/fixtures/api/schemas/entities/issue.json
@@ -5,7 +5,7 @@
"iid": { "type": "integer" },
"author_id": { "type": "integer" },
"description": { "type": ["string", "null"] },
- "lock_version": { "type": ["string", "null"] },
+ "lock_version": { "type": ["integer", "null"] },
"milestone_id": { "type": ["string", "null"] },
"title": { "type": "string" },
"moved_to_id": { "type": ["integer", "null"] },
diff --git a/spec/fixtures/api/schemas/entities/merge_request_basic.json b/spec/fixtures/api/schemas/entities/merge_request_basic.json
index 88a600398b1..ac0a0314455 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_basic.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_basic.json
@@ -23,7 +23,7 @@
},
"task_status": { "type": "string" },
"task_status_short": { "type": "string" },
- "lock_version": { "type": ["string", "null"] }
+ "lock_version": { "type": ["integer", "null"] }
},
"additionalProperties": false
}
diff --git a/spec/frontend/clusters/components/application_row_spec.js b/spec/frontend/clusters/components/application_row_spec.js
index 038d2be9e98..d26fad54ebe 100644
--- a/spec/frontend/clusters/components/application_row_spec.js
+++ b/spec/frontend/clusters/components/application_row_spec.js
@@ -1,6 +1,11 @@
import Vue from 'vue';
import eventHub from '~/clusters/event_hub';
-import { APPLICATION_STATUS, REQUEST_SUBMITTED, REQUEST_FAILURE } from '~/clusters/constants';
+import {
+ APPLICATION_STATUS,
+ REQUEST_SUBMITTED,
+ REQUEST_FAILURE,
+ UPGRADE_REQUESTED,
+} from '~/clusters/constants';
import applicationRow from '~/clusters/components/application_row.vue';
import mountComponent from 'helpers/vue_mount_component_helper';
import { DEFAULT_APPLICATION_STATE } from '../services/mock_data';
@@ -314,6 +319,22 @@ describe('Application Row', () => {
'Update failed. Please check the logs and try again.',
);
});
+
+ it('displays a success toast message if application upgrade was successful', () => {
+ vm = mountComponent(ApplicationRow, {
+ ...DEFAULT_APPLICATION_STATE,
+ title: 'GitLab Runner',
+ requestStatus: UPGRADE_REQUESTED,
+ status: APPLICATION_STATUS.UPDATE_ERRORED,
+ });
+
+ vm.$toast = { show: jest.fn() };
+ vm.status = APPLICATION_STATUS.UPDATED;
+
+ vm.$nextTick(() => {
+ expect(vm.$toast.show).toHaveBeenCalledWith('GitLab Runner upgraded successfully.');
+ });
+ });
});
describe('Version', () => {
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index 841aff0d7ff..3886853f3c1 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -1,7 +1,7 @@
/* eslint no-param-reassign: "off" */
import $ from 'jquery';
-import GfmAutoComplete from '~/gfm_auto_complete';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import 'jquery.caret';
import 'at.js';
@@ -209,6 +209,38 @@ describe('GfmAutoComplete', () => {
});
});
+ describe('DefaultOptions.highlighter', () => {
+ beforeEach(() => {
+ atwhoInstance = { setting: {} };
+ });
+
+ it('should return li if no query is given', () => {
+ const liTag = '<li></li>';
+
+ const highlightedTag = gfmAutoCompleteCallbacks.highlighter.call(atwhoInstance, liTag);
+
+ expect(highlightedTag).toEqual(liTag);
+ });
+
+ it('should highlight search query in li element', () => {
+ const liTag = '<li><img src="" />string</li>';
+ const query = 's';
+
+ const highlightedTag = gfmAutoCompleteCallbacks.highlighter.call(atwhoInstance, liTag, query);
+
+ expect(highlightedTag).toEqual('<li><img src="" /> <strong>s</strong>tring </li>');
+ });
+
+ it('should highlight search query with special char in li element', () => {
+ const liTag = '<li><img src="" />te.st</li>';
+ const query = '.';
+
+ const highlightedTag = gfmAutoCompleteCallbacks.highlighter.call(atwhoInstance, liTag, query);
+
+ expect(highlightedTag).toEqual('<li><img src="" /> te<strong>.</strong>st </li>');
+ });
+ });
+
describe('isLoading', () => {
it('should be true with loading data object item', () => {
expect(GfmAutoComplete.isLoading({ name: 'loading' })).toBe(true);
diff --git a/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js b/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js
index 8e8b7f06ca2..6a1a826093c 100644
--- a/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js
+++ b/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js
@@ -2,6 +2,9 @@ import createState from '~/ide/stores/modules/file_templates/state';
import * as types from '~/ide/stores/modules/file_templates/mutation_types';
import mutations from '~/ide/stores/modules/file_templates/mutations';
+const mockFileTemplates = [['MIT'], ['CC']];
+const mockTemplateType = 'test';
+
describe('IDE file templates mutations', () => {
let state;
@@ -10,11 +13,21 @@ describe('IDE file templates mutations', () => {
});
describe(`${types.REQUEST_TEMPLATE_TYPES}`, () => {
- it('sets isLoading', () => {
+ it('sets loading to true', () => {
+ state.isLoading = false;
+
mutations[types.REQUEST_TEMPLATE_TYPES](state);
expect(state.isLoading).toBe(true);
});
+
+ it('sets templates to an empty array', () => {
+ state.templates = mockFileTemplates;
+
+ mutations[types.REQUEST_TEMPLATE_TYPES](state);
+
+ expect(state.templates).toEqual([]);
+ });
});
describe(`${types.RECEIVE_TEMPLATE_TYPES_ERROR}`, () => {
@@ -31,29 +44,33 @@ describe('IDE file templates mutations', () => {
it('sets isLoading to false', () => {
state.isLoading = true;
- mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, []);
+ mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, mockFileTemplates);
expect(state.isLoading).toBe(false);
});
- it('sets templates', () => {
- mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, ['test']);
+ it('sets templates to payload', () => {
+ state.templates = ['test'];
+
+ mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, mockFileTemplates);
- expect(state.templates).toEqual(['test']);
+ expect(state.templates).toEqual(mockFileTemplates);
});
});
describe(`${types.SET_SELECTED_TEMPLATE_TYPE}`, () => {
- it('sets selectedTemplateType', () => {
- mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, 'type');
+ it('sets templates type to selected type', () => {
+ state.selectedTemplateType = '';
- expect(state.selectedTemplateType).toBe('type');
+ mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, mockTemplateType);
+
+ expect(state.selectedTemplateType).toBe(mockTemplateType);
});
- it('clears templates', () => {
- state.templates = ['test'];
+ it('sets templates to empty array', () => {
+ state.templates = mockFileTemplates;
- mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, 'type');
+ mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, mockTemplateType);
expect(state.templates).toEqual([]);
});
@@ -61,6 +78,8 @@ describe('IDE file templates mutations', () => {
describe(`${types.SET_UPDATE_SUCCESS}`, () => {
it('sets updateSuccess', () => {
+ state.updateSuccess = false;
+
mutations[types.SET_UPDATE_SUCCESS](state, true);
expect(state.updateSuccess).toBe(true);
diff --git a/spec/frontend/mr_popover/index_spec.js b/spec/frontend/mr_popover/index_spec.js
index 8c33e52a04b..b9db2342687 100644
--- a/spec/frontend/mr_popover/index_spec.js
+++ b/spec/frontend/mr_popover/index_spec.js
@@ -7,18 +7,28 @@ createDefaultClient.default = jest.fn();
describe('initMRPopovers', () => {
let mr1;
let mr2;
+ let mr3;
beforeEach(() => {
setHTMLFixture(`
- <div id="one" class="gfm-merge_request">MR1</div>
- <div id="two" class="gfm-merge_request">MR2</div>
+ <div id="one" class="gfm-merge_request" data-mr-title="title" data-iid="1" data-project-path="group/project">
+ MR1
+ </div>
+ <div id="two" class="gfm-merge_request" data-mr-title="title" data-iid="1" data-project-path="group/project">
+ MR2
+ </div>
+ <div id="three" class="gfm-merge_request">
+ MR3
+ </div>
`);
mr1 = document.querySelector('#one');
mr2 = document.querySelector('#two');
+ mr3 = document.querySelector('#three');
mr1.addEventListener = jest.fn();
mr2.addEventListener = jest.fn();
+ mr3.addEventListener = jest.fn();
});
it('does not add the same event listener twice', () => {
@@ -27,4 +37,10 @@ describe('initMRPopovers', () => {
expect(mr1.addEventListener).toHaveBeenCalledTimes(1);
expect(mr2.addEventListener).toHaveBeenCalledTimes(1);
});
+
+ it('does not add listener if it does not have the necessary data attributes', () => {
+ initMRPopovers([mr1, mr2, mr3]);
+
+ expect(mr3.addEventListener).not.toHaveBeenCalled();
+ });
});
diff --git a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js b/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js
index 45f131194ca..eafff7f681e 100644
--- a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js
+++ b/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import issuePlaceholderNote from '~/vue_shared/components/notes/placeholder_note.vue';
import createStore from '~/notes/stores';
-import { userDataMock } from '../../../notes/mock_data';
+import { userDataMock } from '../../../../javascripts/notes/mock_data';
describe('issue placeholder system note component', () => {
let store;
diff --git a/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js b/spec/frontend/vue_shared/components/notes/placeholder_system_note_spec.js
index 6013e85811a..976e38c15ee 100644
--- a/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js
+++ b/spec/frontend/vue_shared/components/notes/placeholder_system_note_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import placeholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import mountComponent from 'helpers/vue_mount_component_helper';
describe('placeholder system note component', () => {
let PlaceholderSystemNote;
diff --git a/spec/javascripts/vue_shared/components/notes/system_note_spec.js b/spec/frontend/vue_shared/components/notes/system_note_spec.js
index adcb1c858aa..adcb1c858aa 100644
--- a/spec/javascripts/vue_shared/components/notes/system_note_spec.js
+++ b/spec/frontend/vue_shared/components/notes/system_note_spec.js
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index a049b5a6133..58eaf991d6e 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -67,27 +67,29 @@ describe LabelsHelper do
describe 'link_to_label' do
let(:project) { create(:project) }
let(:label) { create(:label, project: project) }
+ let(:subject) { nil }
+ let(:label_presenter) { label.present(issuable_subject: subject) }
context 'without subject' do
it "uses the label's project" do
- expect(link_to_label(label)).to match %r{<a href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter)).to match %r{<a href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
context 'with a project as subject' do
let(:namespace) { build(:namespace, name: 'foo3') }
- let(:another_project) { build(:project, namespace: namespace, name: 'bar3') }
+ let(:subject) { build(:project, namespace: namespace, name: 'bar3') }
it 'links to project issues page' do
- expect(link_to_label(label, subject: another_project)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
context 'with a group as subject' do
- let(:group) { build(:group, name: 'bar') }
+ let(:subject) { build(:group, name: 'bar') }
it 'links to group issues page' do
- expect(link_to_label(label, subject: group)).to match %r{<a href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter)).to match %r{<a href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
@@ -95,7 +97,7 @@ describe LabelsHelper do
['issue', :issue, 'merge_request', :merge_request].each do |type|
context "set to #{type}" do
it 'links to correct page' do
- expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
end
@@ -104,14 +106,14 @@ describe LabelsHelper do
context 'with a tooltip argument' do
context 'set to false' do
it 'does not include the has-tooltip class' do
- expect(link_to_label(label, tooltip: false)).not_to match /has-tooltip/
+ expect(link_to_label(label_presenter, tooltip: false)).not_to match /has-tooltip/
end
end
end
context 'with block' do
it 'passes the block to link_to' do
- link = link_to_label(label) { 'Foo' }
+ link = link_to_label(label_presenter) { 'Foo' }
expect(link).to match('Foo')
end
end
@@ -119,8 +121,8 @@ describe LabelsHelper do
context 'without block' do
it 'uses render_colored_label as the link content' do
expect(self).to receive(:render_colored_label)
- .with(label, tooltip: true).and_return('Foo')
- expect(link_to_label(label)).to match('Foo')
+ .with(label_presenter, tooltip: true).and_return('Foo')
+ expect(link_to_label(label_presenter)).to match('Foo')
end
end
end
@@ -237,16 +239,24 @@ describe LabelsHelper do
end
end
- describe 'labels_sorted_by_title' do
+ describe 'presented_labels_sorted_by_title' do
+ let(:labels) do
+ [build(:label, title: 'a'),
+ build(:label, title: 'B'),
+ build(:label, title: 'c'),
+ build(:label, title: 'D')]
+ end
+
it 'sorts labels alphabetically' do
- label1 = double(:label, title: 'a')
- label2 = double(:label, title: 'B')
- label3 = double(:label, title: 'c')
- label4 = double(:label, title: 'D')
- labels = [label1, label2, label3, label4]
-
- expect(labels_sorted_by_title(labels))
- .to match_array([label2, label4, label1, label3])
+ sorted_ids = presented_labels_sorted_by_title(labels, nil).map(&:id)
+
+ expect(sorted_ids)
+ .to match_array([labels[1].id, labels[3].id, labels[0].id, labels[2].id])
+ end
+
+ it 'returns an array of label presenters' do
+ expect(presented_labels_sorted_by_title(labels, nil))
+ .to all(be_a(LabelPresenter))
end
end
diff --git a/spec/javascripts/api_spec.js b/spec/javascripts/api_spec.js
index e537e0e8afc..494b3b934a8 100644
--- a/spec/javascripts/api_spec.js
+++ b/spec/javascripts/api_spec.js
@@ -4,7 +4,7 @@ import Api from '~/api';
describe('Api', () => {
const dummyApiVersion = 'v3000';
- const dummyUrlRoot = 'http://host.invalid';
+ const dummyUrlRoot = '/gitlab';
const dummyGon = {
api_version: dummyApiVersion,
relative_url_root: dummyUrlRoot,
@@ -32,6 +32,18 @@ describe('Api', () => {
expect(builtUrl).toEqual(expectedOutput);
});
+
+ [null, '', '/'].forEach(root => {
+ it(`works when relative_url_root is ${root}`, () => {
+ window.gon.relative_url_root = root;
+ const input = '/api/:version/foo/bar';
+ const expectedOutput = `/api/${dummyApiVersion}/foo/bar`;
+
+ const builtUrl = Api.buildUrl(input);
+
+ expect(builtUrl).toEqual(expectedOutput);
+ });
+ });
});
describe('group', () => {
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index 0d462a6f872..bb6fc6c693d 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -45,6 +45,7 @@ describe('List model', () => {
id: _.random(10000),
title: 'test',
color: 'red',
+ text_color: 'white',
},
});
list.save();
@@ -53,6 +54,8 @@ describe('List model', () => {
expect(list.id).toBe(listObj.id);
expect(list.type).toBe('label');
expect(list.position).toBe(0);
+ expect(list.label.color).toBe('red');
+ expect(list.label.textColor).toBe('white');
done();
}, 0);
});
diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js
index 14fff9223f4..93a0f29af0a 100644
--- a/spec/javascripts/boards/mock_data.js
+++ b/spec/javascripts/boards/mock_data.js
@@ -16,6 +16,7 @@ export const listObj = {
title: 'Testing',
color: 'red',
description: 'testing;',
+ textColor: 'white',
},
};
diff --git a/spec/javascripts/ide/components/new_dropdown/upload_spec.js b/spec/javascripts/ide/components/new_dropdown/upload_spec.js
index 878e17ac805..d19af6af2d7 100644
--- a/spec/javascripts/ide/components/new_dropdown/upload_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/upload_spec.js
@@ -78,6 +78,8 @@ describe('new dropdown upload', () => {
type: 'blob',
content: 'plain text',
base64: false,
+ binary: false,
+ rawPath: '',
});
});
@@ -89,6 +91,8 @@ describe('new dropdown upload', () => {
type: 'blob',
content: binaryTarget.result.split('base64,')[1],
base64: true,
+ binary: true,
+ rawPath: binaryTarget.result,
});
});
});
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index abc97f3072c..cdeb9b4b896 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -4,6 +4,7 @@ import service from '~/ide/services';
import router from '~/ide/ide_router';
import eventHub from '~/ide/eventhub';
import consts from '~/ide/stores/modules/commit/constants';
+import { commitActionTypes } from '~/ide/constants';
import { resetStore, file } from 'spec/ide/helpers';
describe('IDE commit module actions', () => {
@@ -294,7 +295,7 @@ describe('IDE commit module actions', () => {
commit_message: 'testing 123',
actions: [
{
- action: 'update',
+ action: commitActionTypes.update,
file_path: jasmine.anything(),
content: undefined,
encoding: jasmine.anything(),
@@ -321,7 +322,7 @@ describe('IDE commit module actions', () => {
commit_message: 'testing 123',
actions: [
{
- action: 'update',
+ action: commitActionTypes.update,
file_path: jasmine.anything(),
content: undefined,
encoding: jasmine.anything(),
diff --git a/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js b/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js
index 734233100ab..548962c7a92 100644
--- a/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js
@@ -69,18 +69,16 @@ describe('IDE file templates actions', () => {
describe('fetchTemplateTypes', () => {
describe('success', () => {
- let nextPage;
+ const pages = [[{ name: 'MIT' }], [{ name: 'Apache' }], [{ name: 'CC' }]];
beforeEach(() => {
- mock.onGet(/api\/(.*)\/templates\/licenses/).replyOnce(() => [
- 200,
- [
- {
- name: 'MIT',
- },
- ],
- { 'X-NEXT-PAGE': nextPage },
- ]);
+ mock.onGet(/api\/(.*)\/templates\/licenses/).reply(({ params }) => {
+ const pageNum = params.page;
+ const page = pages[pageNum - 1];
+ const hasNextPage = pageNum < pages.length;
+
+ return [200, page, hasNextPage ? { 'X-NEXT-PAGE': pageNum + 1 } : {}];
+ });
});
it('rejects if selectedTemplateType is empty', done => {
@@ -112,43 +110,15 @@ describe('IDE file templates actions', () => {
},
{
type: 'receiveTemplateTypesSuccess',
- payload: [
- {
- name: 'MIT',
- },
- ],
- },
- ],
- done,
- );
- });
-
- it('dispatches actions for next page', done => {
- nextPage = '2';
- state.selectedTemplateType = {
- key: 'licenses',
- };
-
- testAction(
- actions.fetchTemplateTypes,
- null,
- state,
- [],
- [
- {
- type: 'requestTemplateTypes',
+ payload: pages[0],
},
{
type: 'receiveTemplateTypesSuccess',
- payload: [
- {
- name: 'MIT',
- },
- ],
+ payload: pages[0].concat(pages[1]),
},
{
- type: 'fetchTemplateTypes',
- payload: 2,
+ type: 'receiveTemplateTypesSuccess',
+ payload: pages[0].concat(pages[1]).concat(pages[2]),
},
],
done,
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index c4f122efdda..debe1c4acee 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 { commitActionTypes } from '~/ide/constants';
import { file } from '../helpers';
describe('Multi-file store utils', () => {
@@ -107,7 +108,7 @@ describe('Multi-file store utils', () => {
commit_message: 'commit message',
actions: [
{
- action: 'update',
+ action: commitActionTypes.update,
file_path: 'staged',
content: 'updated file content',
encoding: 'text',
@@ -115,7 +116,7 @@ describe('Multi-file store utils', () => {
previous_path: undefined,
},
{
- action: 'create',
+ action: commitActionTypes.create,
file_path: 'added',
content: 'new file content',
encoding: 'base64',
@@ -123,7 +124,7 @@ describe('Multi-file store utils', () => {
previous_path: undefined,
},
{
- action: 'delete',
+ action: commitActionTypes.delete,
file_path: 'deletedFile',
content: undefined,
encoding: 'text',
@@ -170,7 +171,7 @@ describe('Multi-file store utils', () => {
commit_message: 'prebuilt test commit message',
actions: [
{
- action: 'update',
+ action: commitActionTypes.update,
file_path: 'staged',
content: 'updated file content',
encoding: 'text',
@@ -178,7 +179,7 @@ describe('Multi-file store utils', () => {
previous_path: undefined,
},
{
- action: 'create',
+ action: commitActionTypes.create,
file_path: 'added',
content: 'new file content',
encoding: 'base64',
@@ -193,19 +194,19 @@ describe('Multi-file store utils', () => {
describe('commitActionForFile', () => {
it('returns deleted for deleted file', () => {
- expect(utils.commitActionForFile({ deleted: true })).toBe('delete');
+ expect(utils.commitActionForFile({ deleted: true })).toBe(commitActionTypes.delete);
});
it('returns create for tempFile', () => {
- expect(utils.commitActionForFile({ tempFile: true })).toBe('create');
+ expect(utils.commitActionForFile({ tempFile: true })).toBe(commitActionTypes.create);
});
it('returns move for moved file', () => {
- expect(utils.commitActionForFile({ prevPath: 'test' })).toBe('move');
+ expect(utils.commitActionForFile({ prevPath: 'test' })).toBe(commitActionTypes.move);
});
it('returns update by default', () => {
- expect(utils.commitActionForFile({})).toBe('update');
+ expect(utils.commitActionForFile({})).toBe(commitActionTypes.update);
});
});
diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js
index 431798c6ec3..cadcc15385f 100644
--- a/spec/javascripts/merge_request_spec.js
+++ b/spec/javascripts/merge_request_spec.js
@@ -58,7 +58,7 @@ describe('MergeRequest', function() {
{
merge_request: {
description: '- [ ] Task List Item',
- lock_version: undefined,
+ lock_version: 0,
update_task: { line_number: lineNumber, line_source: lineSource, index, checked },
},
},
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index ce2c6c43c0f..16dc0084a10 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -175,7 +175,7 @@ describe('Dashboard', () => {
setTimeout(() => {
const dropdownItems = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item[active="true"]',
+ '.js-environments-dropdown .dropdown-item.is-active',
);
expect(dropdownItems.length).toEqual(1);
diff --git a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
index 4da8c6196b1..bdf802052b9 100644
--- a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
+++ b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
@@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
+import '~/behaviors/markdown/render_gfm';
describe('ContentViewer', () => {
let vm;
@@ -29,6 +30,7 @@ describe('ContentViewer', () => {
path: 'test.md',
content: '* Test',
projectPath: 'testproject',
+ type: 'markdown',
});
const previewContainer = vm.$el.querySelector('.md-previewer');
@@ -44,6 +46,7 @@ describe('ContentViewer', () => {
createComponent({
path: GREEN_BOX_IMAGE_URL,
fileSize: 1024,
+ type: 'image',
});
setTimeout(() => {
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
index 7f2e246d656..97c870f27d9 100644
--- a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
+++ b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
@@ -138,22 +138,6 @@ describe('ImageDiffViewer', () => {
done();
});
});
-
- it('drag handler is working', done => {
- vm.$el.querySelector('.view-modes-menu li:nth-child(2)').click();
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('1px');
- expect(vm.$el.querySelector('.top-handle')).not.toBeNull();
-
- dragSlider(vm.$el.querySelector('.swipe-bar'), 40);
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('-20px');
- done();
- });
- });
- });
});
describe('onionSkin', () => {
diff --git a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js b/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
index 852558a83bc..c7e0d806d80 100644
--- a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
+++ b/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
@@ -61,6 +61,12 @@ describe('User Popover Component', () => {
expect(vm.$el.textContent).toContain(DEFAULT_PROPS.user.username);
expect(vm.$el.textContent).toContain(DEFAULT_PROPS.user.location);
});
+
+ it('shows icon for location', () => {
+ const iconEl = vm.$el.querySelector('.js-location svg');
+
+ expect(iconEl.querySelector('use').getAttribute('xlink:href')).toContain('location');
+ });
});
describe('job data', () => {
@@ -117,6 +123,18 @@ describe('User Popover Component', () => {
'Me & my <funky> Company',
);
});
+
+ it('shows icon for bio', () => {
+ const iconEl = vm.$el.querySelector('.js-bio svg');
+
+ expect(iconEl.querySelector('use').getAttribute('xlink:href')).toContain('profile');
+ });
+
+ it('shows icon for organization', () => {
+ const iconEl = vm.$el.querySelector('.js-organization svg');
+
+ expect(iconEl.querySelector('use').getAttribute('xlink:href')).toContain('work');
+ });
});
describe('status data', () => {
diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb
index 08165f147bb..00916f80784 100644
--- a/spec/lib/api/helpers_spec.rb
+++ b/spec/lib/api/helpers_spec.rb
@@ -137,18 +137,6 @@ describe API::Helpers do
it_behaves_like 'user namespace finder'
end
- describe '#user_namespace' do
- let(:namespace_finder) do
- subject.user_namespace
- end
-
- before do
- allow(subject).to receive(:params).and_return({ id: namespace.id })
- end
-
- it_behaves_like 'user namespace finder'
- end
-
describe '#send_git_blob' do
let(:repository) { double }
let(:blob) { double(name: 'foobar') }
diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
index e7ff9169f1b..d3f7f1ded16 100644
--- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
+++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
@@ -177,7 +177,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
end
before do
- allow_any_instance_of(described_class::MergeRequestDiff::ActiveRecord_Relation)
+ allow_any_instance_of(ActiveRecord::Relation)
.to receive(:update_all).and_raise(exception)
end
diff --git a/spec/lib/gitlab/checks/branch_check_spec.rb b/spec/lib/gitlab/checks/branch_check_spec.rb
index 8d5ab27a17c..71b64a3b9df 100644
--- a/spec/lib/gitlab/checks/branch_check_spec.rb
+++ b/spec/lib/gitlab/checks/branch_check_spec.rb
@@ -77,117 +77,85 @@ describe Gitlab::Checks::BranchCheck do
let(:oldrev) { '0000000000000000000000000000000000000000' }
let(:ref) { 'refs/heads/feature' }
- context 'protected branch creation feature is disabled' do
+ context 'user can push to branch' do
before do
- stub_feature_flags(protected_branch_creation: false)
+ allow(user_access)
+ .to receive(:can_push_to_branch?)
+ .with('feature')
+ .and_return(true)
end
- context 'user is not allowed to push to protected branch' do
- before do
- allow(user_access)
- .to receive(:can_push_to_branch?)
- .and_return(false)
- end
-
- it 'raises an error' do
- expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to protected branches on this project.')
- end
+ it 'does not raise an error' do
+ expect { subject.validate! }.not_to raise_error
end
+ end
- context 'user is allowed to push to protected branch' do
- before do
- allow(user_access)
- .to receive(:can_push_to_branch?)
- .and_return(true)
- end
-
- it 'does not raise an error' do
- expect { subject.validate! }.not_to raise_error
- end
+ context 'user cannot push to branch' do
+ before do
+ allow(user_access)
+ .to receive(:can_push_to_branch?)
+ .with('feature')
+ .and_return(false)
end
- end
- context 'protected branch creation feature is enabled' do
- context 'user can push to branch' do
+ context 'user cannot merge to branch' do
before do
allow(user_access)
- .to receive(:can_push_to_branch?)
+ .to receive(:can_merge_to_branch?)
.with('feature')
- .and_return(true)
+ .and_return(false)
end
- it 'does not raise an error' do
- expect { subject.validate! }.not_to raise_error
+ it 'raises an error' do
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to create protected branches on this project.')
end
end
- context 'user cannot push to branch' do
+ context 'user can merge to branch' do
before do
allow(user_access)
- .to receive(:can_push_to_branch?)
+ .to receive(:can_merge_to_branch?)
.with('feature')
- .and_return(false)
+ .and_return(true)
+
+ allow(project.repository)
+ .to receive(:branch_names_contains_sha)
+ .with(newrev)
+ .and_return(['branch'])
end
- context 'user cannot merge to branch' do
+ context "newrev isn't in any protected branches" do
before do
- allow(user_access)
- .to receive(:can_merge_to_branch?)
- .with('feature')
+ allow(ProtectedBranch)
+ .to receive(:any_protected?)
+ .with(project, ['branch'])
.and_return(false)
end
it 'raises an error' do
- expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to create protected branches on this project.')
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only use an existing protected branch ref as the basis of a new protected branch.')
end
end
- context 'user can merge to branch' do
+ context 'newrev is included in a protected branch' do
before do
- allow(user_access)
- .to receive(:can_merge_to_branch?)
- .with('feature')
+ allow(ProtectedBranch)
+ .to receive(:any_protected?)
+ .with(project, ['branch'])
.and_return(true)
-
- allow(project.repository)
- .to receive(:branch_names_contains_sha)
- .with(newrev)
- .and_return(['branch'])
end
- context "newrev isn't in any protected branches" do
- before do
- allow(ProtectedBranch)
- .to receive(:any_protected?)
- .with(project, ['branch'])
- .and_return(false)
- end
+ context 'via web interface' do
+ let(:protocol) { 'web' }
- it 'raises an error' do
- expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only use an existing protected branch ref as the basis of a new protected branch.')
+ it 'allows branch creation' do
+ expect { subject.validate! }.not_to raise_error
end
end
- context 'newrev is included in a protected branch' do
- before do
- allow(ProtectedBranch)
- .to receive(:any_protected?)
- .with(project, ['branch'])
- .and_return(true)
- end
-
- context 'via web interface' do
- let(:protocol) { 'web' }
-
- it 'allows branch creation' do
- expect { subject.validate! }.not_to raise_error
- end
- end
-
- context 'via SSH' do
- it 'raises an error' do
- expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only create protected branches using the web interface and API.')
- end
+ context 'via SSH' do
+ it 'raises an error' do
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only create protected branches using the web interface and API.')
end
end
end
diff --git a/spec/lib/gitlab/checks/lfs_check_spec.rb b/spec/lib/gitlab/checks/lfs_check_spec.rb
index 35f8069c8a4..dad14e100a7 100644
--- a/spec/lib/gitlab/checks/lfs_check_spec.rb
+++ b/spec/lib/gitlab/checks/lfs_check_spec.rb
@@ -27,6 +27,18 @@ describe Gitlab::Checks::LfsCheck do
allow(project).to receive(:lfs_enabled?).and_return(true)
end
+ context 'with lfs_check feature disabled' do
+ before do
+ stub_feature_flags(lfs_check: false)
+ end
+
+ it 'skips integrity check' do
+ expect_any_instance_of(Gitlab::Git::LfsChanges).not_to receive(:new_pointers)
+
+ subject.validate!
+ end
+ end
+
context 'deletion' do
let(:changes) { { oldrev: oldrev, ref: ref } }
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb
new file mode 100644
index 00000000000..9aa2f4efd67
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotEquals do
+ let(:left) { double('left') }
+ let(:right) { double('right') }
+
+ describe '.build' do
+ it 'creates a new instance of the token' do
+ expect(described_class.build('!=', left, right))
+ .to be_a(described_class)
+ end
+ end
+
+ describe '.type' do
+ it 'is an operator' do
+ expect(described_class.type).to eq :operator
+ end
+ end
+
+ describe '#evaluate' do
+ it 'returns true when left and right are not equal' do
+ allow(left).to receive(:evaluate).and_return(1)
+ allow(right).to receive(:evaluate).and_return(2)
+
+ operator = described_class.new(left, right)
+
+ expect(operator.evaluate(VARIABLE: 3)).to eq true
+ end
+
+ it 'returns false when left and right are equal' do
+ allow(left).to receive(:evaluate).and_return(1)
+ allow(right).to receive(:evaluate).and_return(1)
+
+ operator = described_class.new(left, right)
+
+ expect(operator.evaluate(VARIABLE: 3)).to eq false
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
new file mode 100644
index 00000000000..fa3b9651fb4
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
@@ -0,0 +1,80 @@
+require 'fast_spec_helper'
+require_dependency 're2'
+
+describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotMatches do
+ let(:left) { double('left') }
+ let(:right) { double('right') }
+
+ describe '.build' do
+ it 'creates a new instance of the token' do
+ expect(described_class.build('!~', left, right))
+ .to be_a(described_class)
+ end
+ end
+
+ describe '.type' do
+ it 'is an operator' do
+ expect(described_class.type).to eq :operator
+ end
+ end
+
+ describe '#evaluate' do
+ it 'returns true when left and right do not match' do
+ allow(left).to receive(:evaluate).and_return('my-string')
+ allow(right).to receive(:evaluate)
+ .and_return(Gitlab::UntrustedRegexp.new('something'))
+
+ operator = described_class.new(left, right)
+
+ expect(operator.evaluate).to eq true
+ end
+
+ it 'returns false when left and right match' do
+ allow(left).to receive(:evaluate).and_return('my-awesome-string')
+ allow(right).to receive(:evaluate)
+ .and_return(Gitlab::UntrustedRegexp.new('awesome.string$'))
+
+ operator = described_class.new(left, right)
+
+ expect(operator.evaluate).to eq false
+ end
+
+ it 'supports matching against a nil value' do
+ allow(left).to receive(:evaluate).and_return(nil)
+ allow(right).to receive(:evaluate)
+ .and_return(Gitlab::UntrustedRegexp.new('pattern'))
+
+ operator = described_class.new(left, right)
+
+ expect(operator.evaluate).to eq true
+ end
+
+ it 'supports multiline strings' do
+ allow(left).to receive(:evaluate).and_return <<~TEXT
+ My awesome contents
+
+ My-text-string!
+ TEXT
+
+ allow(right).to receive(:evaluate)
+ .and_return(Gitlab::UntrustedRegexp.new('text-string'))
+
+ operator = described_class.new(left, right)
+
+ expect(operator.evaluate).to eq false
+ end
+
+ it 'supports regexp flags' do
+ allow(left).to receive(:evaluate).and_return <<~TEXT
+ My AWESOME content
+ TEXT
+
+ allow(right).to receive(:evaluate)
+ .and_return(Gitlab::UntrustedRegexp.new('(?i)awesome'))
+
+ operator = described_class.new(left, right)
+
+ expect(operator.evaluate).to eq false
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
index 11e73294f18..a9fd809409b 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
@@ -101,6 +101,18 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do
"$EMPTY_VARIABLE =~ /var.*/" | false
"$UNDEFINED_VARIABLE =~ /var.*/" | false
"$PRESENT_VARIABLE =~ /VAR.*/i" | true
+ '$PRESENT_VARIABLE != "my variable"' | false
+ '"my variable" != $PRESENT_VARIABLE' | false
+ '$PRESENT_VARIABLE != null' | true
+ '$EMPTY_VARIABLE != null' | true
+ '"" != $EMPTY_VARIABLE' | false
+ '$UNDEFINED_VARIABLE != null' | false
+ 'null != $UNDEFINED_VARIABLE' | false
+ "$PRESENT_VARIABLE !~ /var.*e$/" | false
+ "$PRESENT_VARIABLE !~ /^var.*/" | true
+ "$EMPTY_VARIABLE !~ /var.*/" | true
+ "$UNDEFINED_VARIABLE !~ /var.*/" | true
+ "$PRESENT_VARIABLE !~ /VAR.*/i" | false
end
with_them do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 10bc82e24d1..1c24244c3a6 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -341,7 +341,7 @@ describe Gitlab::Git::Blob, :seed_helper do
it { expect(blob.mode).to eq("100755") }
end
- context 'file with Chinese text' do
+ context 'file with Japanese text' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/テスト.txt") }
it { expect(blob.name).to eq("テスト.txt") }
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 778950c95e4..45fe5d72937 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1966,6 +1966,70 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
+ describe '#compare_source_branch' do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '', 'group/project') }
+
+ context 'within same repository' do
+ it 'does not create a temp ref' do
+ expect(repository).not_to receive(:fetch_source_branch!)
+ expect(repository).not_to receive(:delete_refs)
+
+ compare = repository.compare_source_branch('master', repository, 'feature', straight: false)
+ expect(compare).to be_a(Gitlab::Git::Compare)
+ expect(compare.commits.count).to be > 0
+ end
+
+ it 'returns empty commits when source ref does not exist' do
+ compare = repository.compare_source_branch('master', repository, 'non-existent-branch', straight: false)
+
+ expect(compare.commits).to be_empty
+ end
+ end
+
+ context 'with different repositories' do
+ context 'when ref is known by source repo, but not by target' do
+ before do
+ mutable_repository.write_ref('another-branch', 'feature')
+ end
+
+ it 'creates temp ref' do
+ expect(repository).not_to receive(:fetch_source_branch!)
+ expect(repository).not_to receive(:delete_refs)
+
+ compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
+ expect(compare).to be_a(Gitlab::Git::Compare)
+ expect(compare.commits.count).to be > 0
+ end
+ end
+
+ context 'when ref is known by source and target repos' do
+ before do
+ mutable_repository.write_ref('another-branch', 'feature')
+ repository.write_ref('another-branch', 'feature')
+ end
+
+ it 'does not create a temp ref' do
+ expect(repository).not_to receive(:fetch_source_branch!)
+ expect(repository).not_to receive(:delete_refs)
+
+ compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false)
+ expect(compare).to be_a(Gitlab::Git::Compare)
+ expect(compare.commits.count).to be > 0
+ end
+ end
+
+ context 'when ref is unknown by source repo' do
+ it 'returns nil when source ref does not exist' do
+ expect(repository).to receive(:fetch_source_branch!).and_call_original
+ expect(repository).to receive(:delete_refs).and_call_original
+
+ compare = repository.compare_source_branch('master', mutable_repository, 'non-existent-branch', straight: false)
+ expect(compare).to be_nil
+ end
+ end
+ end
+ end
+
describe '#checksum' do
it 'calculates the checksum for non-empty repo' do
expect(repository.checksum).to eq '51d0a9662681f93e1fee547a6b7ba2bcaf716059'
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index ded5d7576df..1e577392949 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -21,13 +21,13 @@ describe Gitlab::Git::Wiki do
end
it 'returns all the pages' do
- expect(subject.pages.count).to eq(2)
- expect(subject.pages.first.title).to eq 'page1'
- expect(subject.pages.last.title).to eq 'page2'
+ expect(subject.list_pages.count).to eq(2)
+ expect(subject.list_pages.first.title).to eq 'page1'
+ expect(subject.list_pages.last.title).to eq 'page2'
end
it 'returns only one page' do
- pages = subject.pages(limit: 1)
+ pages = subject.list_pages(limit: 1)
expect(pages.count).to eq(1)
expect(pages.first.title).to eq 'page1'
@@ -62,8 +62,8 @@ describe Gitlab::Git::Wiki do
subject.delete_page('*', commit_details('whatever'))
- expect(subject.pages.count).to eq 1
- expect(subject.pages.first.title).to eq 'page1'
+ expect(subject.list_pages.count).to eq 1
+ expect(subject.list_pages.first.title).to eq 'page1'
end
end
@@ -87,7 +87,7 @@ describe Gitlab::Git::Wiki do
with_them do
subject { wiki.preview_slug(title, format) }
- let(:gitaly_slug) { wiki.pages.first }
+ let(:gitaly_slug) { wiki.list_pages.first }
it { is_expected.to eq(expected_slug) }
@@ -96,7 +96,7 @@ describe Gitlab::Git::Wiki do
create_page(title, 'content', format: format)
- gitaly_slug = wiki.pages.first.url_path
+ gitaly_slug = wiki.list_pages.first.url_path
is_expected.to eq(gitaly_slug)
end
diff --git a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
index d82c9c28da0..4fa8e97aca0 100644
--- a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
@@ -46,7 +46,7 @@ describe Gitlab::GitalyClient::WikiService do
end
end
- describe '#get_all_pages' do
+ describe '#load_all_pages' do
let(:page_2_info) { { title: 'My Page 2', raw_data: 'c', version: page_version } }
let(:response) do
[
@@ -63,7 +63,7 @@ describe Gitlab::GitalyClient::WikiService do
let(:wiki_page_2) { subject[1].first }
let(:wiki_page_2_version) { subject[1].last }
- subject { client.get_all_pages }
+ subject { client.load_all_pages }
it 'sends a wiki_get_all_pages message' do
expect_any_instance_of(Gitaly::WikiService::Stub)
@@ -99,7 +99,7 @@ describe Gitlab::GitalyClient::WikiService do
end
context 'with limits' do
- subject { client.get_all_pages(limit: 1) }
+ subject { client.load_all_pages(limit: 1) }
it 'sends a request with the limit' do
expect_any_instance_of(Gitaly::WikiService::Stub)
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
index 95a4eb296fb..aec9c4baf0a 100644
--- a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
+++ b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
@@ -45,7 +45,7 @@ describe Gitlab::Graphql::Authorize::AuthorizeFieldService do
end
end
- context "when the field is a scalar type" do
+ context "when the field is a built-in scalar type" do
let(:field) { type_with_field(GraphQL::STRING_TYPE, :read_field).fields["testField"].to_graphql }
let(:expected_permissions) { [:read_field] }
@@ -58,6 +58,20 @@ describe Gitlab::Graphql::Authorize::AuthorizeFieldService do
it_behaves_like "checking permissions on the presented object"
end
+
+ context "when the field is sub-classed scalar type" do
+ let(:field) { type_with_field(Types::TimeType, :read_field).fields["testField"].to_graphql }
+ let(:expected_permissions) { [:read_field] }
+
+ it_behaves_like "checking permissions on the presented object"
+ end
+
+ context "when the field is a list of sub-classed scalar types" do
+ let(:field) { type_with_field([Types::TimeType], :read_field).fields["testField"].to_graphql }
+ let(:expected_permissions) { [:read_field] }
+
+ it_behaves_like "checking permissions on the presented object"
+ end
end
context "when the field is a specific type" do
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
index ec17ad8541f..7c4ac62790e 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
@@ -32,5 +32,17 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
strategy.execute(user, project)
end
+
+ context 'when upload fails' do
+ it 'stores the export error' do
+ stub_request(:post, example_url).to_return(status: [404, 'Page not found'])
+
+ strategy.execute(user, project)
+
+ errors = project.import_export_shared.errors
+ expect(errors).not_to be_empty
+ expect(errors.first).to eq "Error uploading the project. Code 404: Page not found"
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
index 87ab81d8169..ddfbb020a55 100644
--- a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
+++ b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
@@ -29,7 +29,7 @@ describe 'Import/Export attribute configuration' do
it 'has no new columns' do
relation_names.each do |relation_name|
relation_class = relation_class_for_name(relation_name)
- relation_attributes = relation_class.new.attributes.keys
+ relation_attributes = relation_class.new.attributes.keys - relation_class.encrypted_attributes.keys.map(&:to_s)
current_attributes = parsed_attributes(relation_name, relation_attributes)
safe_attributes = safe_model_attributes[relation_class.to_s].dup || []
diff --git a/spec/lib/quality/seeders/issues_spec.rb b/spec/lib/quality/seeders/issues_spec.rb
new file mode 100644
index 00000000000..e17414a541a
--- /dev/null
+++ b/spec/lib/quality/seeders/issues_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Quality::Seeders::Issues do
+ let(:project) { create(:project) }
+
+ subject { described_class.new(project: project) }
+
+ describe '#seed' do
+ it 'seeds issues' do
+ issues_created = subject.seed(backfill_weeks: 1, average_issues_per_week: 1)
+
+ expect(issues_created).to be_between(0, 2)
+ expect(project.issues.count).to eq(issues_created)
+ end
+ end
+end
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb
index d0e1688cce3..8364293b908 100644
--- a/spec/models/blob_spec.rb
+++ b/spec/models/blob_spec.rb
@@ -43,6 +43,21 @@ describe Blob do
changelog.id
contributing.id
end
+
+ it 'does not include blobs from previous requests in later requests' do
+ changelog = described_class.lazy(project, commit_id, 'CHANGELOG')
+ contributing = described_class.lazy(same_project, commit_id, 'CONTRIBUTING.md')
+
+ # Access property so the values are loaded
+ changelog.id
+ contributing.id
+
+ readme = described_class.lazy(project, commit_id, 'README.md')
+
+ expect(project.repository).to receive(:blobs_at).with([[commit_id, 'README.md']]).once.and_call_original
+
+ readme.id
+ end
end
describe '#data' do
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index aacfbe3f180..44b5af5e5aa 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -33,7 +33,7 @@ describe Ci::Bridge do
CI_PROJECT_ID CI_PROJECT_NAME CI_PROJECT_PATH
CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PIPELINE_IID
CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE
- CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION
+ CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION CI_COMMIT_REF_PROTECTED
]
expect(bridge.scoped_variables_hash.keys).to include(*variables)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 95b9bd4a4d9..3a7d20a58c8 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2183,7 +2183,8 @@ describe Ci::Build do
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true, masked: false },
{ key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true, masked: false },
{ key: 'CI_COMMIT_TITLE', value: pipeline.git_commit_title, public: true, masked: false },
- { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true, masked: false }
+ { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true, masked: false },
+ { key: 'CI_COMMIT_REF_PROTECTED', value: (!!pipeline.protected_ref?).to_s, public: true, masked: false }
]
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index fd66e344d63..3c823b78be7 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -690,7 +690,8 @@ describe Ci::Pipeline, :mailer do
CI_PIPELINE_SOURCE
CI_COMMIT_MESSAGE
CI_COMMIT_TITLE
- CI_COMMIT_DESCRIPTION]
+ CI_COMMIT_DESCRIPTION
+ CI_COMMIT_REF_PROTECTED]
end
context 'when source is merge request' do
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 2cb581696a0..f735a89f69f 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -73,9 +73,8 @@ describe Ci::Runner do
end
it 'fails to save a group assigned to a project runner even if the runner is already saved' do
- group_runner
-
- expect { create(:group, runners: [project_runner]) }
+ group.runners << project_runner
+ expect { group.save! }
.to raise_error(ActiveRecord::RecordInvalid)
end
end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 0ed4e146caa..806b4f61bd8 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -93,7 +93,7 @@ describe InternalId do
before do
described_class.reset_column_information
# Project factory will also call the current_version
- expect(ActiveRecord::Migrator).to receive(:current_version).twice.and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
+ expect(ActiveRecord::Migrator).to receive(:current_version).at_least(:once).and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
end
let(:init) { double('block') }
@@ -104,6 +104,15 @@ describe InternalId do
expect(init).to receive(:call).with(issue).and_return(val)
expect(subject).to eq(val + 1)
end
+
+ it 'always attempts to generate internal IDs in production mode' do
+ allow(Rails.env).to receive(:test?).and_return(false)
+ val = rand(1..100)
+ generator = double(generate: val)
+ expect(InternalId::InternalIdGenerator).to receive(:new).and_return(generator)
+
+ expect(subject).to eq(val)
+ end
end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 2525a6aebe0..d12dd97bb9e 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -109,8 +109,7 @@ describe ProjectWiki do
subject { super().empty? }
it { is_expected.to be_falsey }
- # Re-enable this when https://gitlab.com/gitlab-org/gitaly/issues/1204 is fixed
- xit 'only instantiates a Wiki page once' do
+ it 'only instantiates a Wiki page once' do
expect(WikiPage).to receive(:new).once.and_call_original
subject
@@ -119,22 +118,65 @@ describe ProjectWiki do
end
end
- describe "#pages" do
+ describe "#list_pages" do
+ let(:wiki_pages) { subject.list_pages }
+
before do
- create_page("index", "This is an awesome new Gollum Wiki")
- @pages = subject.pages
+ create_page("index", "This is an index")
+ create_page("index2", "This is an index2")
+ create_page("an index3", "This is an index3")
end
after do
- destroy_page(@pages.first.page)
+ wiki_pages.each do |wiki_page|
+ destroy_page(wiki_page.page)
+ end
end
it "returns an array of WikiPage instances" do
- expect(@pages.first).to be_a WikiPage
+ expect(wiki_pages.first).to be_a WikiPage
+ end
+
+ it 'does not load WikiPage content by default' do
+ wiki_pages.each do |page|
+ expect(page.content).to be_empty
+ end
+ end
+
+ it 'returns all pages by default' do
+ expect(wiki_pages.count).to eq(3)
+ end
+
+ context "with limit option" do
+ it 'returns limited set of pages' do
+ expect(subject.list_pages(limit: 1).count).to eq(1)
+ end
end
- it "returns the correct number of pages" do
- expect(@pages.count).to eq(1)
+ context "with sorting options" do
+ it 'returns pages sorted by title by default' do
+ pages = ['an index3', 'index', 'index2']
+
+ expect(subject.list_pages.map(&:title)).to eq(pages)
+ expect(subject.list_pages(direction: "desc").map(&:title)).to eq(pages.reverse)
+ end
+
+ it 'returns pages sorted by created_at' do
+ pages = ['index', 'index2', 'an index3']
+
+ expect(subject.list_pages(sort: 'created_at').map(&:title)).to eq(pages)
+ expect(subject.list_pages(sort: 'created_at', direction: "desc").map(&:title)).to eq(pages.reverse)
+ end
+ end
+
+ context "with load_content option" do
+ let(:pages) { subject.list_pages(load_content: true) }
+
+ it 'loads WikiPage content' do
+ expect(pages.first.content).to eq("This is an index3")
+ expect(pages.second.content).to eq("This is an index")
+ expect(pages.third.content).to eq("This is an index2")
+ end
end
end
@@ -144,7 +186,7 @@ describe ProjectWiki do
end
after do
- subject.pages.each { |page| destroy_page(page.page) }
+ subject.list_pages.each { |page| destroy_page(page.page) }
end
it "returns the latest version of the page if it exists" do
@@ -195,7 +237,7 @@ describe ProjectWiki do
end
after do
- subject.pages.each { |page| destroy_page(page.page) }
+ subject.list_pages.each { |page| destroy_page(page.page) }
end
it 'finds the page defined as _sidebar' do
@@ -242,12 +284,12 @@ describe ProjectWiki do
describe "#create_page" do
after do
- destroy_page(subject.pages.first.page)
+ destroy_page(subject.list_pages.first.page)
end
it "creates a new wiki page" do
expect(subject.create_page("test page", "this is content")).not_to eq(false)
- expect(subject.pages.count).to eq(1)
+ expect(subject.list_pages.count).to eq(1)
end
it "returns false when a duplicate page exists" do
@@ -262,7 +304,7 @@ describe ProjectWiki do
it "sets the correct commit message" do
subject.create_page("test page", "some content", :markdown, "commit message")
- expect(subject.pages.first.page.version.message).to eq("commit message")
+ expect(subject.list_pages.first.page.version.message).to eq("commit message")
end
it 'sets the correct commit email' do
@@ -293,7 +335,7 @@ describe ProjectWiki do
format: :markdown,
message: "updated page"
)
- @page = subject.pages.first.page
+ @page = subject.list_pages(load_content: true).first.page
end
after do
@@ -337,7 +379,7 @@ describe ProjectWiki do
it "deletes the page" do
subject.delete_page(@page)
- expect(subject.pages.count).to eq(0)
+ expect(subject.list_pages.count).to eq(0)
end
it 'sets the correct commit email' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 3f5d285bc2c..4c354593b57 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -217,6 +217,25 @@ describe Repository do
expect(result.size).to eq(0)
end
+
+ context 'with a commit with invalid UTF-8 path' do
+ def create_commit_with_invalid_utf8_path
+ rugged = rugged_repo(repository)
+ blob_id = Rugged::Blob.from_buffer(rugged, "some contents")
+ tree_builder = Rugged::Tree::Builder.new(rugged)
+ tree_builder.insert({ oid: blob_id, name: "hello\x80world", filemode: 0100644 })
+ tree_id = tree_builder.write
+ user = { email: "jcai@gitlab.com", time: Time.now, name: "John Cai" }
+
+ Rugged::Commit.create(rugged, message: 'some commit message', parents: [rugged.head.target.oid], tree: tree_id, committer: user, author: user)
+ end
+
+ it 'does not raise an error' do
+ commit = create_commit_with_invalid_utf8_path
+
+ expect { repository.list_last_commits_for_tree(commit, '.', offset: 0) }.not_to raise_error
+ end
+ end
end
describe '#last_commit_for_path' do
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index d5c85c11195..520a06e138e 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -44,47 +44,49 @@ describe WikiPage do
WikiDirectory.new('dir_2', pages)
end
- context 'sort by title' do
- let(:grouped_entries) { described_class.group_by_directory(wiki.pages) }
- let(:expected_grouped_entries) { [dir_1_1, dir_1, page_dir_2, dir_2, page_1, page_6] }
-
- it 'returns an array with pages and directories' do
- grouped_entries.each_with_index do |page_or_dir, i|
- expected_page_or_dir = expected_grouped_entries[i]
- expected_slugs = get_slugs(expected_page_or_dir)
- slugs = get_slugs(page_or_dir)
-
- expect(slugs).to match_array(expected_slugs)
+ context "#list_pages" do
+ context 'sort by title' do
+ let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages) }
+ let(:expected_grouped_entries) { [dir_1_1, dir_1, page_dir_2, dir_2, page_1, page_6] }
+
+ it 'returns an array with pages and directories' do
+ grouped_entries.each_with_index do |page_or_dir, i|
+ expected_page_or_dir = expected_grouped_entries[i]
+ expected_slugs = get_slugs(expected_page_or_dir)
+ slugs = get_slugs(page_or_dir)
+
+ expect(slugs).to match_array(expected_slugs)
+ end
end
end
- end
- context 'sort by created_at' do
- let(:grouped_entries) { described_class.group_by_directory(wiki.pages(sort: 'created_at')) }
- let(:expected_grouped_entries) { [dir_1_1, page_1, dir_1, page_dir_2, dir_2, page_6] }
+ context 'sort by created_at' do
+ let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages(sort: 'created_at')) }
+ let(:expected_grouped_entries) { [dir_1_1, page_1, dir_1, page_dir_2, dir_2, page_6] }
- it 'returns an array with pages and directories' do
- grouped_entries.each_with_index do |page_or_dir, i|
- expected_page_or_dir = expected_grouped_entries[i]
- expected_slugs = get_slugs(expected_page_or_dir)
- slugs = get_slugs(page_or_dir)
+ it 'returns an array with pages and directories' do
+ grouped_entries.each_with_index do |page_or_dir, i|
+ expected_page_or_dir = expected_grouped_entries[i]
+ expected_slugs = get_slugs(expected_page_or_dir)
+ slugs = get_slugs(page_or_dir)
- expect(slugs).to match_array(expected_slugs)
+ expect(slugs).to match_array(expected_slugs)
+ end
end
end
- end
- it 'returns an array with retained order with directories at the top' do
- expected_order = ['dir_1/dir_1_1/page_3', 'dir_1/page_2', 'dir_2', 'dir_2/page_4', 'dir_2/page_5', 'page_1', 'page_6']
+ it 'returns an array with retained order with directories at the top' do
+ expected_order = ['dir_1/dir_1_1/page_3', 'dir_1/page_2', 'dir_2', 'dir_2/page_4', 'dir_2/page_5', 'page_1', 'page_6']
- grouped_entries = described_class.group_by_directory(wiki.pages)
+ grouped_entries = described_class.group_by_directory(wiki.list_pages)
- actual_order =
- grouped_entries.map do |page_or_dir|
- get_slugs(page_or_dir)
- end
- .flatten
- expect(actual_order).to eq(expected_order)
+ actual_order =
+ grouped_entries.map do |page_or_dir|
+ get_slugs(page_or_dir)
+ end
+ .flatten
+ expect(actual_order).to eq(expected_order)
+ end
end
end
end
@@ -386,7 +388,7 @@ describe WikiPage do
it "deletes the page" do
@page.delete
- expect(wiki.pages).to be_empty
+ expect(wiki.list_pages).to be_empty
end
it "returns true" do
diff --git a/spec/presenters/label_presenter_spec.rb b/spec/presenters/label_presenter_spec.rb
new file mode 100644
index 00000000000..fae8188670f
--- /dev/null
+++ b/spec/presenters/label_presenter_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe LabelPresenter do
+ include Gitlab::Routing.url_helpers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, group: group) }
+ let(:label) { build_stubbed(:label, project: project).present(issuable_subject: project) }
+ let(:group_label) { build_stubbed(:group_label, group: group).present(issuable_subject: project) }
+
+ describe '#edit_path' do
+ context 'with group label' do
+ subject { group_label.edit_path }
+
+ it { is_expected.to eq(edit_group_label_path(group, group_label)) }
+ end
+
+ context 'with project label' do
+ subject { label.edit_path }
+
+ it { is_expected.to eq(edit_project_label_path(project, label)) }
+ end
+ end
+
+ describe '#destroy_path' do
+ context 'with group label' do
+ subject { group_label.destroy_path }
+
+ it { is_expected.to eq(group_label_path(group, group_label)) }
+ end
+
+ context 'with project label' do
+ subject { label.destroy_path }
+
+ it { is_expected.to eq(project_label_path(project, label)) }
+ end
+ end
+
+ describe '#filter_path' do
+ context 'with group as context subject' do
+ let(:label_in_group) { build_stubbed(:label, project: project).present(issuable_subject: group) }
+ subject { label_in_group.filter_path }
+
+ it { is_expected.to eq(issues_group_path(group, label_name: [label_in_group.title])) }
+ end
+
+ context 'with project as context subject' do
+ subject { label.filter_path }
+
+ it { is_expected.to eq(namespace_project_issues_path(group, project, label_name: [label.title])) }
+ end
+ end
+
+ describe '#can_subscribe_to_label_in_different_levels?' do
+ it 'returns true for group labels in project context' do
+ expect(group_label.can_subscribe_to_label_in_different_levels?).to be_truthy
+ end
+
+ it 'returns false for project labels in project context' do
+ expect(label.can_subscribe_to_label_in_different_levels?).to be_falsey
+ end
+ end
+end
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 29515709a74..b331da1acba 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -68,7 +68,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
post api('/runners'), params: { token: group.runners_token }
expect(response).to have_http_status 201
- expect(group.runners.size).to eq(1)
+ expect(group.runners.reload.size).to eq(1)
runner = Ci::Runner.first
expect(runner.token).not_to eq(registration_token)
expect(runner.token).not_to eq(group.runners_token)
diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb
index 49021f5d1b7..a12646ea222 100644
--- a/spec/requests/rack_attack_global_spec.rb
+++ b/spec/requests/rack_attack_global_spec.rb
@@ -251,8 +251,8 @@ describe 'Rack Attack global throttles' do
let(:throttle_setting_prefix) { 'throttle_authenticated_web' }
context 'with the token in the query string' do
- let(:get_args) { [rss_url(user), nil] }
- let(:other_user_get_args) { [rss_url(other_user), nil] }
+ let(:get_args) { [rss_url(user), params: nil] }
+ let(:other_user_get_args) { [rss_url(other_user), params: nil] }
it_behaves_like 'rate-limited token-authenticated requests'
end
diff --git a/spec/services/compare_service_spec.rb b/spec/services/compare_service_spec.rb
index 0e4ef69ec19..fadd43635a6 100644
--- a/spec/services/compare_service_spec.rb
+++ b/spec/services/compare_service_spec.rb
@@ -19,5 +19,18 @@ describe CompareService do
it { expect(subject.diffs.size).to eq(3) }
end
+
+ context 'compare with target branch that does not exist' do
+ subject { service.execute(project, 'non-existent-ref') }
+
+ it { expect(subject).to be_nil }
+ end
+
+ context 'compare with source branch that does not exist' do
+ let(:service) { described_class.new(project, 'non-existent-branch') }
+ subject { service.execute(project, 'non-existent-ref') }
+
+ it { expect(subject).to be_nil }
+ end
end
end
diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb
index 43a70d33d2d..b8064c2cbc1 100644
--- a/spec/services/delete_branch_service_spec.rb
+++ b/spec/services/delete_branch_service_spec.rb
@@ -8,20 +8,24 @@ describe DeleteBranchService do
let(:user) { create(:user) }
let(:service) { described_class.new(project, user) }
+ shared_examples 'a deleted branch' do |branch_name|
+ it 'removes the branch' do
+ expect(branch_exists?(branch_name)).to be true
+
+ result = service.execute(branch_name)
+
+ expect(result.status).to eq :success
+ expect(branch_exists?(branch_name)).to be false
+ end
+ end
+
describe '#execute' do
context 'when user has access to push to repository' do
before do
project.add_developer(user)
end
- it 'removes the branch' do
- expect(branch_exists?('feature')).to be true
-
- result = service.execute('feature')
-
- expect(result[:status]).to eq :success
- expect(branch_exists?('feature')).to be false
- end
+ it_behaves_like 'a deleted branch', 'feature'
end
context 'when user does not have access to push to repository' do
@@ -30,8 +34,8 @@ describe DeleteBranchService do
result = service.execute('feature')
- expect(result[:status]).to eq :error
- expect(result[:message]).to eq 'You dont have push access to repo'
+ expect(result.status).to eq :error
+ expect(result.message).to eq 'You dont have push access to repo'
expect(branch_exists?('feature')).to be true
end
end
diff --git a/spec/services/merge_requests/merge_to_ref_service_spec.rb b/spec/services/merge_requests/merge_to_ref_service_spec.rb
index 24d09c1fd00..0ac23050caf 100644
--- a/spec/services/merge_requests/merge_to_ref_service_spec.rb
+++ b/spec/services/merge_requests/merge_to_ref_service_spec.rb
@@ -104,7 +104,7 @@ describe MergeRequests::MergeToRefService do
it_behaves_like 'MergeService for target ref'
end
- context 'when merge commit with squash' do
+ context 'when merge commit with squash', :quarantine do
before do
merge_request.update!(squash: true, source_branch: 'master', target_branch: 'feature')
end
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index c37147e7d3b..5ad30b58511 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -440,6 +440,8 @@ describe Projects::UpdateService do
context 'when auto devops is set to instance setting' do
before do
project.create_auto_devops!(enabled: nil)
+ project.reload
+
allow(project.auto_devops).to receive(:previous_changes).and_return('enabled' => true)
end
diff --git a/spec/services/service_response_spec.rb b/spec/services/service_response_spec.rb
new file mode 100644
index 00000000000..30bd4d6820b
--- /dev/null
+++ b/spec/services/service_response_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+ActiveSupport::Dependencies.autoload_paths << 'app/services'
+
+describe ServiceResponse do
+ describe '.success' do
+ it 'creates a successful response without a message' do
+ expect(described_class.success).to be_success
+ end
+
+ it 'creates a successful response with a message' do
+ response = described_class.success(message: 'Good orange')
+
+ expect(response).to be_success
+ expect(response.message).to eq('Good orange')
+ end
+ end
+
+ describe '.error' do
+ it 'creates a failed response without HTTP status' do
+ response = described_class.error(message: 'Bad apple')
+
+ expect(response).to be_error
+ expect(response.message).to eq('Bad apple')
+ end
+
+ it 'creates a failed response with HTTP status' do
+ response = described_class.error(message: 'Bad apple', http_status: 400)
+
+ expect(response).to be_error
+ expect(response.message).to eq('Bad apple')
+ expect(response.http_status).to eq(400)
+ end
+ end
+
+ describe '#success?' do
+ it 'returns true for a successful response' do
+ expect(described_class.success.success?).to eq(true)
+ end
+
+ it 'returns false for a failed response' do
+ expect(described_class.error(message: 'Bad apple').success?).to eq(false)
+ end
+ end
+
+ describe '#error?' do
+ it 'returns false for a successful response' do
+ expect(described_class.success.error?).to eq(false)
+ end
+
+ it 'returns true for a failed response' do
+ expect(described_class.error(message: 'Bad apple').error?).to eq(true)
+ end
+ end
+end
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index 1c79af34538..4a5f4509a7b 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -210,6 +210,8 @@ describe Users::DestroyService do
describe "calls the before/after callbacks" do
it 'of project_members' do
+ expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:find).once
+ expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:initialize).once
expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:destroy).once
service.execute(user)
@@ -219,6 +221,8 @@ describe Users::DestroyService do
group_member = create(:group_member)
group_member.group.group_members.create(user: user, access_level: 40)
+ expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:find).once
+ expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:initialize).once
expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:destroy).once
service.execute(user)
diff --git a/spec/services/users/migrate_to_ghost_user_service_spec.rb b/spec/services/users/migrate_to_ghost_user_service_spec.rb
index b808fa6d91a..40206775aed 100644
--- a/spec/services/users/migrate_to_ghost_user_service_spec.rb
+++ b/spec/services/users/migrate_to_ghost_user_service_spec.rb
@@ -80,7 +80,7 @@ describe Users::MigrateToGhostUserService do
context "when record migration fails with a rollback exception" do
before do
- expect_any_instance_of(MergeRequest::ActiveRecord_Associations_CollectionProxy)
+ expect_any_instance_of(ActiveRecord::Associations::CollectionProxy)
.to receive(:update_all).and_raise(ActiveRecord::Rollback)
end
diff --git a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
index 72912ffb89d..a0c77eecb61 100644
--- a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
+++ b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
@@ -25,9 +25,13 @@ shared_context 'Ldap::OmniauthCallbacksController' do
described_class.define_providers!
Rails.application.reload_routes!
- mock_auth_hash(provider.to_s, uid, user.email)
+ @original_env_config_omniauth_auth = mock_auth_hash(provider.to_s, uid, user.email)
stub_omniauth_provider(provider, context: request)
allow(Gitlab::Auth::LDAP::Access).to receive(:allowed?).and_return(valid_login?)
end
+
+ after do
+ Rails.application.env_config['omniauth.auth'] = @original_env_config_omniauth_auth
+ end
end
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index 4a0cf62a661..0bb2d2510c2 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -118,7 +118,10 @@ module LoginHelpers
response_object: response_object
}
})
+ original_env_config_omniauth_auth = Rails.application.env_config['omniauth.auth']
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym]
+
+ original_env_config_omniauth_auth
end
def saml_xml(raw_saml_response)
diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb
index 7ce63375d34..c4ae62b25e4 100644
--- a/spec/support/helpers/query_recorder.rb
+++ b/spec/support/helpers/query_recorder.rb
@@ -17,7 +17,7 @@ module ActiveRecord
def callback(name, start, finish, message_id, values)
show_backtrace(values) if ENV['QUERY_RECORDER_DEBUG']
- if values[:name]&.include?("CACHE") && skip_cached
+ if values[:cached] && skip_cached
@cached << values[:sql]
elsif !values[:name]&.include?("SCHEMA")
@log << values[:sql]
diff --git a/spec/support/helpers/test_request_helpers.rb b/spec/support/helpers/test_request_helpers.rb
index 5a84d67bdfc..39e5dafb059 100644
--- a/spec/support/helpers/test_request_helpers.rb
+++ b/spec/support/helpers/test_request_helpers.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module TestRequestHelpers
- def test_request(remote_ip: '127.0.0.1')
- ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new)
+ def test_request(remote_ip: '127.0.0.1', controller: nil)
+ ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new, controller)
end
end
diff --git a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
index 62ae95df8c0..1284415da1f 100644
--- a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
+++ b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
@@ -45,7 +45,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user
context "race conditions" do
context "when #{record_class_name} migration fails and is rolled back" do
before do
- expect_any_instance_of(record_class::ActiveRecord_Associations_CollectionProxy)
+ expect_any_instance_of(ActiveRecord::Associations::CollectionProxy)
.to receive(:update_all).and_raise(ActiveRecord::Rollback)
end
@@ -66,7 +66,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user
context "when #{record_class_name} migration fails with a non-rollback exception" do
before do
- expect_any_instance_of(record_class::ActiveRecord_Associations_CollectionProxy)
+ expect_any_instance_of(ActiveRecord::Associations::CollectionProxy)
.to receive(:update_all).and_raise(ArgumentError)
end
diff --git a/yarn.lock b/yarn.lock
index ebbb670db0c..a0446b652ac 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -663,10 +663,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.59.0.tgz#affcf9596d736836d37469bb4aea2226ac03e087"
integrity sha512-dokGyyLRRsoBKO70KP1g+ZsDGyTK/RIHWDmvWI6Bx5AxQ3UqAzVXn2OIb3owjJAexyRG1uBmJrriiVVyHznQ4g==
-"@gitlab/ui@^3.3.0":
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-3.3.0.tgz#32112c8eb53a0fd893f8fa431a02ceaf55279323"
- integrity sha512-VVInZmcAe0L0lRMb6II3ED4DYm4OpzSmcxdwt18CqpDMw3EEoUqxd58EAxBFy70isgFTd6cHYhyS8rw5GOULyg==
+"@gitlab/ui@^3.5.0":
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-3.5.0.tgz#31ecfc16e3f7663545f31ddf07e02bba96a6d138"
+ integrity sha512-eDD++hhGJuH59g2QcGshuou9/NLcLfse4Abm9KOIWIaYI3NPWW2KRGwLHPB6H0d5W0/X5pyWYQvXgF7JE2ZXbA==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "^2.0.0-rc.11"
@@ -678,6 +678,7 @@
url-search-params-polyfill "^5.0.0"
vue "^2.5.21"
vue-loader "^15.4.2"
+ vue-toasted "^1.1.26"
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
@@ -10982,6 +10983,11 @@ vue-template-es2015-compiler@^1.9.0:
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
+vue-toasted@^1.1.26:
+ version "1.1.26"
+ resolved "https://registry.yarnpkg.com/vue-toasted/-/vue-toasted-1.1.26.tgz#1333d1a42157ab78389c3810023a49ba94e69c7b"
+ integrity sha512-Z4/gfPcqdzsRvif7UITrZOkh3C6jm0yQKJyr9kX31IGWXor5dNipE1Sc5SnlL5RLmY7vlLa+SqIjc9Gbpy7V0g==
+
vue-virtual-scroll-list@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/vue-virtual-scroll-list/-/vue-virtual-scroll-list-1.3.1.tgz#efcb83d3a3dcc69cd886fa4de1130a65493e8f76"