diff options
109 files changed, 1456 insertions, 246 deletions
diff --git a/.haml-lint.yml b/.haml-lint.yml index e356be30c45..0412b24a48c 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -1,9 +1,13 @@ +inherits_from: + - .haml-lint_todo.yml # Whether to ignore frontmatter at the beginning of HAML documents for # frameworks such as Jekyll/Middleman skip_frontmatter: false exclude: - 'vendor/**/*' - 'spec/**/*' +require: + - './haml_lint/linter/no_plain_nodes.rb' linters: AltText: diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml new file mode 100644 index 00000000000..61805c2d8d2 --- /dev/null +++ b/.haml-lint_todo.yml @@ -0,0 +1,523 @@ +# This configuration was generated by +# `haml-lint --auto-gen-config` +# on 2019-05-07 19:04:08 +0100 using Haml-Lint version 0.30.0. +# The point is for the user to remove these configuration records +# one by one as the lints are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of Haml-Lint, may require this file to be generated again. + +linters: + + # Offense count: 2075 + NoPlainNodes: + enabled: true + exclude: + - "app/views/admin/abuse_reports/_abuse_report.html.haml" + - "app/views/admin/abuse_reports/index.html.haml" + - "app/views/admin/appearances/_form.html.haml" + - "app/views/admin/application_settings/_abuse.html.haml" + - "app/views/admin/application_settings/_diff_limits.html.haml" + - "app/views/admin/application_settings/_gitaly.html.haml" + - "app/views/admin/application_settings/_influx.html.haml" + - "app/views/admin/application_settings/_ip_limits.html.haml" + - "app/views/admin/application_settings/_logging.html.haml" + - "app/views/admin/application_settings/_performance.html.haml" + - "app/views/admin/application_settings/_plantuml.html.haml" + - "app/views/admin/application_settings/_prometheus.html.haml" + - "app/views/admin/application_settings/_realtime.html.haml" + - "app/views/admin/application_settings/_repository_check.html.haml" + - "app/views/admin/application_settings/_repository_storage.html.haml" + - "app/views/admin/application_settings/_signin.html.haml" + - "app/views/admin/application_settings/_signup.html.haml" + - "app/views/admin/application_settings/_spam.html.haml" + - "app/views/admin/application_settings/_terminal.html.haml" + - "app/views/admin/application_settings/_usage.html.haml" + - "app/views/admin/application_settings/_visibility_and_access.html.haml" + - "app/views/admin/applications/_delete_form.html.haml" + - "app/views/admin/applications/_form.html.haml" + - "app/views/admin/applications/edit.html.haml" + - "app/views/admin/applications/index.html.haml" + - "app/views/admin/applications/new.html.haml" + - "app/views/admin/applications/show.html.haml" + - "app/views/admin/background_jobs/show.html.haml" + - "app/views/admin/broadcast_messages/index.html.haml" + - "app/views/admin/dashboard/index.html.haml" + - "app/views/admin/deploy_keys/new.html.haml" + - "app/views/admin/groups/show.html.haml" + - "app/views/admin/health_check/show.html.haml" + - "app/views/admin/hook_logs/_index.html.haml" + - "app/views/admin/hook_logs/show.html.haml" + - "app/views/admin/hooks/_form.html.haml" + - "app/views/admin/hooks/edit.html.haml" + - "app/views/admin/hooks/index.html.haml" + - "app/views/admin/labels/_form.html.haml" + - "app/views/admin/logs/show.html.haml" + - "app/views/admin/projects/_projects.html.haml" + - "app/views/admin/projects/show.html.haml" + - "app/views/admin/requests_profiles/index.html.haml" + - "app/views/admin/runners/_runner.html.haml" + - "app/views/admin/runners/index.html.haml" + - "app/views/admin/runners/show.html.haml" + - "app/views/admin/services/_form.html.haml" + - "app/views/admin/services/index.html.haml" + - "app/views/admin/spam_logs/_spam_log.html.haml" + - "app/views/admin/spam_logs/index.html.haml" + - "app/views/admin/system_info/show.html.haml" + - "app/views/admin/users/_access_levels.html.haml" + - "app/views/admin/users/_form.html.haml" + - "app/views/admin/users/_head.html.haml" + - "app/views/admin/users/_profile.html.haml" + - "app/views/admin/users/_projects.html.haml" + - "app/views/admin/users/new.html.haml" + - "app/views/admin/users/projects.html.haml" + - "app/views/admin/users/show.html.haml" + - "app/views/clusters/clusters/_cluster.html.haml" + - "app/views/clusters/clusters/_form.html.haml" + - "app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml" + - "app/views/clusters/clusters/gcp/_form.html.haml" + - "app/views/clusters/clusters/new.html.haml" + - "app/views/dashboard/milestones/index.html.haml" + - "app/views/dashboard/projects/_blank_state_admin_welcome.html.haml" + - "app/views/dashboard/projects/_blank_state_welcome.html.haml" + - "app/views/dashboard/projects/_zero_authorized_projects.html.haml" + - "app/views/dashboard/snippets/index.html.haml" + - "app/views/dashboard/todos/_todo.html.haml" + - "app/views/dashboard/todos/index.html.haml" + - "app/views/devise/confirmations/almost_there.haml" + - "app/views/devise/mailer/_confirmation_instructions_account.html.haml" + - "app/views/devise/mailer/_confirmation_instructions_secondary.html.haml" + - "app/views/devise/mailer/email_changed.html.haml" + - "app/views/devise/mailer/password_change.html.haml" + - "app/views/devise/mailer/reset_password_instructions.html.haml" + - "app/views/devise/mailer/unlock_instructions.html.haml" + - "app/views/devise/passwords/edit.html.haml" + - "app/views/devise/sessions/_new_base.html.haml" + - "app/views/devise/sessions/_new_crowd.html.haml" + - "app/views/devise/sessions/_new_ldap.html.haml" + - "app/views/devise/sessions/new.html.haml" + - "app/views/devise/sessions/two_factor.html.haml" + - "app/views/devise/shared/_omniauth_box.html.haml" + - "app/views/devise/shared/_sign_in_link.html.haml" + - "app/views/devise/shared/_signup_box.html.haml" + - "app/views/devise/shared/_tabs_normal.html.haml" + - "app/views/discussions/_discussion.html.haml" + - "app/views/discussions/_headline.html.haml" + - "app/views/discussions/_notes.html.haml" + - "app/views/discussions/_resolve_all.html.haml" + - "app/views/doorkeeper/applications/_delete_form.html.haml" + - "app/views/doorkeeper/authorized_applications/_delete_form.html.haml" + - "app/views/errors/encoding.html.haml" + - "app/views/errors/git_not_found.html.haml" + - "app/views/errors/omniauth_error.html.haml" + - "app/views/errors/precondition_failed.html.haml" + - "app/views/events/_commit.html.haml" + - "app/views/events/_event_push.atom.haml" + - "app/views/events/event/_push.html.haml" + - "app/views/groups/_create_chat_team.html.haml" + - "app/views/groups/_group_admin_settings.html.haml" + - "app/views/groups/group_members/_new_group_member.html.haml" + - "app/views/groups/group_members/index.html.haml" + - "app/views/groups/labels/edit.html.haml" + - "app/views/groups/labels/new.html.haml" + - "app/views/groups/milestones/edit.html.haml" + - "app/views/groups/milestones/index.html.haml" + - "app/views/groups/milestones/new.html.haml" + - "app/views/groups/projects.html.haml" + - "app/views/groups/runners/edit.html.haml" + - "app/views/groups/settings/_advanced.html.haml" + - "app/views/groups/settings/_lfs.html.haml" + - "app/views/help/_shortcuts.html.haml" + - "app/views/help/index.html.haml" + - "app/views/help/instance_configuration.html.haml" + - "app/views/help/instance_configuration/_gitlab_ci.html.haml" + - "app/views/help/instance_configuration/_gitlab_pages.html.haml" + - "app/views/help/instance_configuration/_ssh_info.html.haml" + - "app/views/help/ui.html.haml" + - "app/views/import/bitbucket/status.html.haml" + - "app/views/import/bitbucket_server/status.html.haml" + - "app/views/instance_statistics/cohorts/_cohorts_table.html.haml" + - "app/views/instance_statistics/cohorts/_usage_ping.html.haml" + - "app/views/invites/show.html.haml" + - "app/views/layouts/_mailer.html.haml" + - "app/views/layouts/header/_default.html.haml" + - "app/views/layouts/header/_new_dropdown.haml" + - "app/views/layouts/mailer/devise.html.haml" + - "app/views/layouts/nav/sidebar/_profile.html.haml" + - "app/views/layouts/notify.html.haml" + - "app/views/notify/_failed_builds.html.haml" + - "app/views/notify/_reassigned_issuable_email.html.haml" + - "app/views/notify/_removal_notification.html.haml" + - "app/views/notify/autodevops_disabled_email.html.haml" + - "app/views/notify/changed_milestone_email.html.haml" + - "app/views/notify/import_issues_csv_email.html.haml" + - "app/views/notify/issue_moved_email.html.haml" + - "app/views/notify/member_access_denied_email.html.haml" + - "app/views/notify/member_invite_accepted_email.html.haml" + - "app/views/notify/member_invite_declined_email.html.haml" + - "app/views/notify/member_invited_email.html.haml" + - "app/views/notify/new_gpg_key_email.html.haml" + - "app/views/notify/new_mention_in_issue_email.html.haml" + - "app/views/notify/new_ssh_key_email.html.haml" + - "app/views/notify/new_user_email.html.haml" + - "app/views/notify/pages_domain_disabled_email.html.haml" + - "app/views/notify/pages_domain_enabled_email.html.haml" + - "app/views/notify/pages_domain_verification_failed_email.html.haml" + - "app/views/notify/pages_domain_verification_succeeded_email.html.haml" + - "app/views/notify/pipeline_failed_email.html.haml" + - "app/views/notify/pipeline_success_email.html.haml" + - "app/views/notify/project_was_exported_email.html.haml" + - "app/views/notify/project_was_moved_email.html.haml" + - "app/views/notify/project_was_not_exported_email.html.haml" + - "app/views/notify/push_to_merge_request_email.html.haml" + - "app/views/notify/remote_mirror_update_failed_email.html.haml" + - "app/views/notify/removed_milestone_issue_email.html.haml" + - "app/views/notify/removed_milestone_merge_request_email.html.haml" + - "app/views/notify/repository_push_email.html.haml" + - "app/views/peek/views/_gc.html.haml" + - "app/views/peek/views/_redis.html.haml" + - "app/views/peek/views/_sidekiq.html.haml" + - "app/views/profiles/_event_table.html.haml" + - "app/views/profiles/active_sessions/_active_session.html.haml" + - "app/views/profiles/active_sessions/index.html.haml" + - "app/views/profiles/audit_log.html.haml" + - "app/views/profiles/chat_names/_chat_name.html.haml" + - "app/views/profiles/chat_names/index.html.haml" + - "app/views/profiles/chat_names/new.html.haml" + - "app/views/profiles/emails/index.html.haml" + - "app/views/profiles/gpg_keys/_key.html.haml" + - "app/views/profiles/gpg_keys/index.html.haml" + - "app/views/profiles/keys/_key.html.haml" + - "app/views/profiles/keys/_key_details.html.haml" + - "app/views/profiles/keys/index.html.haml" + - "app/views/profiles/notifications/show.html.haml" + - "app/views/profiles/passwords/edit.html.haml" + - "app/views/profiles/personal_access_tokens/index.html.haml" + - "app/views/profiles/preferences/show.html.haml" + - "app/views/profiles/show.html.haml" + - "app/views/profiles/two_factor_auths/_codes.html.haml" + - "app/views/profiles/two_factor_auths/codes.html.haml" + - "app/views/profiles/two_factor_auths/create.html.haml" + - "app/views/profiles/two_factor_auths/show.html.haml" + - "app/views/projects/_bitbucket_import_modal.html.haml" + - "app/views/projects/_customize_workflow.html.haml" + - "app/views/projects/_deletion_failed.html.haml" + - "app/views/projects/_fork_suggestion.html.haml" + - "app/views/projects/_gitlab_import_modal.html.haml" + - "app/views/projects/_home_panel.html.haml" + - "app/views/projects/_import_project_pane.html.haml" + - "app/views/projects/_issuable_by_email.html.haml" + - "app/views/projects/_md_preview.html.haml" + - "app/views/projects/_new_project_fields.html.haml" + - "app/views/projects/_readme.html.haml" + - "app/views/projects/artifacts/_tree_file.html.haml" + - "app/views/projects/artifacts/browse.html.haml" + - "app/views/projects/blame/_age_map_legend.html.haml" + - "app/views/projects/blame/show.html.haml" + - "app/views/projects/blob/_editor.html.haml" + - "app/views/projects/blob/_header_content.html.haml" + - "app/views/projects/blob/_new_dir.html.haml" + - "app/views/projects/blob/_remove.html.haml" + - "app/views/projects/blob/_render_error.html.haml" + - "app/views/projects/blob/_template_selectors.html.haml" + - "app/views/projects/blob/_upload.html.haml" + - "app/views/projects/blob/edit.html.haml" + - "app/views/projects/blob/new.html.haml" + - "app/views/projects/blob/preview.html.haml" + - "app/views/projects/blob/viewers/_empty.html.haml" + - "app/views/projects/blob/viewers/_stl.html.haml" + - "app/views/projects/branches/_branch.html.haml" + - "app/views/projects/branches/_commit.html.haml" + - "app/views/projects/branches/_delete_protected_modal.html.haml" + - "app/views/projects/branches/new.html.haml" + - "app/views/projects/ci/builds/_build.html.haml" + - "app/views/projects/ci/lints/_create.html.haml" + - "app/views/projects/commit/_change.html.haml" + - "app/views/projects/commits/_commit.html.haml" + - "app/views/projects/commits/_inline_commit.html.haml" + - "app/views/projects/compare/_form.html.haml" + - "app/views/projects/compare/index.html.haml" + - "app/views/projects/cycle_analytics/_empty_stage.html.haml" + - "app/views/projects/cycle_analytics/_no_access.html.haml" + - "app/views/projects/cycle_analytics/_overview.html.haml" + - "app/views/projects/cycle_analytics/show.html.haml" + - "app/views/projects/deploy_keys/_form.html.haml" + - "app/views/projects/deploy_keys/_index.html.haml" + - "app/views/projects/deploy_keys/edit.html.haml" + - "app/views/projects/deploy_tokens/_revoke_modal.html.haml" + - "app/views/projects/deploy_tokens/_table.html.haml" + - "app/views/projects/deployments/_deployment.html.haml" + - "app/views/projects/diffs/_file_header.html.haml" + - "app/views/projects/diffs/_replaced_image_diff.html.haml" + - "app/views/projects/diffs/_stats.html.haml" + - "app/views/projects/empty.html.haml" + - "app/views/projects/environments/show.html.haml" + - "app/views/projects/forks/error.html.haml" + - "app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml" + - "app/views/projects/graphs/charts.html.haml" + - "app/views/projects/hook_logs/_index.html.haml" + - "app/views/projects/hook_logs/show.html.haml" + - "app/views/projects/hooks/_index.html.haml" + - "app/views/projects/hooks/edit.html.haml" + - "app/views/projects/imports/new.html.haml" + - "app/views/projects/imports/show.html.haml" + - "app/views/projects/issues/_issue.html.haml" + - "app/views/projects/issues/_new_branch.html.haml" + - "app/views/projects/issues/import_csv/_modal.html.haml" + - "app/views/projects/issues/show.html.haml" + - "app/views/projects/jobs/_header.html.haml" + - "app/views/projects/jobs/_table.html.haml" + - "app/views/projects/jobs/index.html.haml" + - "app/views/projects/labels/edit.html.haml" + - "app/views/projects/labels/new.html.haml" + - "app/views/projects/mattermosts/_no_teams.html.haml" + - "app/views/projects/mattermosts/_team_selection.html.haml" + - "app/views/projects/mattermosts/new.html.haml" + - "app/views/projects/merge_requests/_commits.html.haml" + - "app/views/projects/merge_requests/_discussion.html.haml" + - "app/views/projects/merge_requests/_how_to_merge.html.haml" + - "app/views/projects/merge_requests/_merge_request.html.haml" + - "app/views/projects/merge_requests/_mr_title.html.haml" + - "app/views/projects/merge_requests/conflicts/_commit_stats.html.haml" + - "app/views/projects/merge_requests/conflicts/_file_actions.html.haml" + - "app/views/projects/merge_requests/conflicts/_submit_form.html.haml" + - "app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml" + - "app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml" + - "app/views/projects/merge_requests/conflicts/show.html.haml" + - "app/views/projects/merge_requests/creations/_diffs.html.haml" + - "app/views/projects/merge_requests/creations/_new_compare.html.haml" + - "app/views/projects/merge_requests/creations/_new_submit.html.haml" + - "app/views/projects/merge_requests/diffs/_different_base.html.haml" + - "app/views/projects/merge_requests/diffs/_diffs.html.haml" + - "app/views/projects/merge_requests/diffs/_version_controls.html.haml" + - "app/views/projects/merge_requests/invalid.html.haml" + - "app/views/projects/merge_requests/widget/open/_error.html.haml" + - "app/views/projects/mirrors/_regenerate_public_ssh_key_confirm_modal.html.haml" + - "app/views/projects/mirrors/_ssh_host_keys.html.haml" + - "app/views/projects/new.html.haml" + - "app/views/projects/no_repo.html.haml" + - "app/views/projects/pages/_access.html.haml" + - "app/views/projects/pages/_destroy.haml" + - "app/views/projects/pages/_https_only.html.haml" + - "app/views/projects/pages/_list.html.haml" + - "app/views/projects/pages/_no_domains.html.haml" + - "app/views/projects/pages/_use.html.haml" + - "app/views/projects/pages/show.html.haml" + - "app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml" + - "app/views/projects/pipelines/_info.html.haml" + - "app/views/projects/pipelines/charts/_pipelines.haml" + - "app/views/projects/protected_branches/shared/_branches_list.html.haml" + - "app/views/projects/protected_branches/shared/_create_protected_branch.html.haml" + - "app/views/projects/protected_branches/shared/_dropdown.html.haml" + - "app/views/projects/protected_branches/shared/_index.html.haml" + - "app/views/projects/protected_branches/shared/_matching_branch.html.haml" + - "app/views/projects/protected_branches/shared/_protected_branch.html.haml" + - "app/views/projects/protected_branches/show.html.haml" + - "app/views/projects/protected_tags/shared/_create_protected_tag.html.haml" + - "app/views/projects/protected_tags/shared/_dropdown.html.haml" + - "app/views/projects/protected_tags/shared/_index.html.haml" + - "app/views/projects/protected_tags/shared/_matching_tag.html.haml" + - "app/views/projects/protected_tags/shared/_protected_tag.html.haml" + - "app/views/projects/protected_tags/shared/_tags_list.html.haml" + - "app/views/projects/protected_tags/show.html.haml" + - "app/views/projects/registry/repositories/_tag.html.haml" + - "app/views/projects/repositories/_feed.html.haml" + - "app/views/projects/runners/_shared_runners.html.haml" + - "app/views/projects/runners/edit.html.haml" + - "app/views/projects/services/_form.html.haml" + - "app/views/projects/services/_index.html.haml" + - "app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml" + - "app/views/projects/services/mattermost_slash_commands/_help.html.haml" + - "app/views/projects/services/prometheus/_metrics.html.haml" + - "app/views/projects/services/slack_slash_commands/_help.html.haml" + - "app/views/projects/settings/ci_cd/_badge.html.haml" + - "app/views/projects/settings/ci_cd/_form.html.haml" + - "app/views/projects/stage/_stage.html.haml" + - "app/views/projects/tags/index.html.haml" + - "app/views/projects/tags/new.html.haml" + - "app/views/projects/tags/releases/edit.html.haml" + - "app/views/projects/tree/_tree_row.html.haml" + - "app/views/projects/tree/_truncated_notice_tree_row.html.haml" + - "app/views/projects/triggers/_content.html.haml" + - "app/views/projects/triggers/_form.html.haml" + - "app/views/projects/triggers/_index.html.haml" + - "app/views/projects/triggers/_trigger.html.haml" + - "app/views/projects/triggers/edit.html.haml" + - "app/views/projects/wikis/_new.html.haml" + - "app/views/projects/wikis/_pages_wiki_page.html.haml" + - "app/views/projects/wikis/edit.html.haml" + - "app/views/projects/wikis/history.html.haml" + - "app/views/repository_check_mailer/notify.html.haml" + - "app/views/search/_form.html.haml" + - "app/views/search/results/_issue.html.haml" + - "app/views/search/results/_note.html.haml" + - "app/views/search/results/_snippet_blob.html.haml" + - "app/views/search/results/_snippet_title.html.haml" + - "app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml" + - "app/views/shared/_commit_message_container.html.haml" + - "app/views/shared/_confirm_modal.html.haml" + - "app/views/shared/_delete_label_modal.html.haml" + - "app/views/shared/_group_form.html.haml" + - "app/views/shared/_group_tips.html.haml" + - "app/views/shared/_milestone_expired.html.haml" + - "app/views/shared/_no_password.html.haml" + - "app/views/shared/_no_ssh.html.haml" + - "app/views/shared/_outdated_browser.html.haml" + - "app/views/shared/_personal_access_tokens_created_container.html.haml" + - "app/views/shared/_personal_access_tokens_table.html.haml" + - "app/views/shared/_ping_consent.html.haml" + - "app/views/shared/_project_limit.html.haml" + - "app/views/shared/_service_settings.html.haml" + - "app/views/shared/boards/components/_board.html.haml" + - "app/views/shared/boards/components/_sidebar.html.haml" + - "app/views/shared/boards/components/sidebar/_due_date.html.haml" + - "app/views/shared/boards/components/sidebar/_labels.html.haml" + - "app/views/shared/boards/components/sidebar/_milestone.html.haml" + - "app/views/shared/empty_states/_priority_labels.html.haml" + - "app/views/shared/hook_logs/_content.html.haml" + - "app/views/shared/issuable/_assignees.html.haml" + - "app/views/shared/issuable/_board_create_list_dropdown.html.haml" + - "app/views/shared/issuable/_bulk_update_sidebar.html.haml" + - "app/views/shared/issuable/_close_reopen_report_toggle.html.haml" + - "app/views/shared/issuable/_form.html.haml" + - "app/views/shared/issuable/_search_bar.html.haml" + - "app/views/shared/issuable/_sidebar.html.haml" + - "app/views/shared/issuable/form/_default_templates.html.haml" + - "app/views/shared/issuable/form/_issue_assignee.html.haml" + - "app/views/shared/issuable/form/_template_selector.html.haml" + - "app/views/shared/issuable/form/_title.html.haml" + - "app/views/shared/labels/_form.html.haml" + - "app/views/shared/members/_member.html.haml" + - "app/views/shared/milestones/_form_dates.html.haml" + - "app/views/shared/milestones/_issuable.html.haml" + - "app/views/shared/milestones/_milestone.html.haml" + - "app/views/shared/milestones/_sidebar.html.haml" + - "app/views/shared/milestones/_top.html.haml" + - "app/views/shared/notes/_hints.html.haml" + - "app/views/shared/notes/_note.html.haml" + - "app/views/shared/notifications/_button.html.haml" + - "app/views/shared/notifications/_custom_notifications.html.haml" + - "app/views/shared/notifications/_new_button.html.haml" + - "app/views/shared/notifications/_notification_dropdown.html.haml" + - "app/views/shared/plugins/_index.html.haml" + - "app/views/shared/projects/_dropdown.html.haml" + - "app/views/shared/projects/_list.html.haml" + - "app/views/shared/projects/_project.html.haml" + - "app/views/shared/runners/_runner_description.html.haml" + - "app/views/shared/runners/show.html.haml" + - "app/views/shared/snippets/_embed.html.haml" + - "app/views/shared/snippets/_header.html.haml" + - "app/views/shared/snippets/_snippet.html.haml" + - "app/views/shared/tokens/_scopes_list.html.haml" + - "app/views/shared/web_hooks/_form.html.haml" + - "app/views/shared/web_hooks/_test_button.html.haml" + - "app/views/u2f/_authenticate.html.haml" + - "app/views/u2f/_register.html.haml" + - "app/views/users/_deletion_guidance.html.haml" + - "ee/app/views/admin/_namespace_plan_info.html.haml" + - "ee/app/views/admin/application_settings/_elasticsearch_form.html.haml" + - "ee/app/views/admin/application_settings/_slack.html.haml" + - "ee/app/views/admin/application_settings/_snowplow.html.haml" + - "ee/app/views/admin/application_settings/_templates.html.haml" + - "ee/app/views/admin/audit_logs/index.html.haml" + - "ee/app/views/admin/dashboard/stats.html.haml" + - "ee/app/views/admin/emails/show.html.haml" + - "ee/app/views/admin/geo/nodes/edit.html.haml" + - "ee/app/views/admin/geo/nodes/new.html.haml" + - "ee/app/views/admin/geo/projects/_registry_failed.html.haml" + - "ee/app/views/admin/geo/projects/_registry_never.html.haml" + - "ee/app/views/admin/licenses/_breakdown.html.haml" + - "ee/app/views/admin/licenses/_upload_trial_license.html.haml" + - "ee/app/views/admin/licenses/missing.html.haml" + - "ee/app/views/admin/licenses/new.html.haml" + - "ee/app/views/admin/licenses/show.html.haml" + - "ee/app/views/admin/monitoring/ee/_nav.html.haml" + - "ee/app/views/admin/projects/_shared_runner_status.html.haml" + - "ee/app/views/admin/push_rules/show.html.haml" + - "ee/app/views/admin/users/_limits.html.haml" + - "ee/app/views/admin/users/_user_detail_note.html.haml" + - "ee/app/views/dashboard/projects/_blank_state_ee_trial.html.haml" + - "ee/app/views/errors/kerberos_denied.html.haml" + - "ee/app/views/groups/analytics/show.html.haml" + - "ee/app/views/groups/audit_events/index.html.haml" + - "ee/app/views/groups/ee/_settings_nav.html.haml" + - "ee/app/views/groups/epics/_epic.html.haml" + - "ee/app/views/groups/group_members/_ldap_sync.html.haml" + - "ee/app/views/groups/group_members/_sync_button.html.haml" + - "ee/app/views/groups/hooks/_project_hook.html.haml" + - "ee/app/views/groups/hooks/index.html.haml" + - "ee/app/views/groups/ldap_group_links/index.html.haml" + - "ee/app/views/groups/pipeline_quota/index.html.haml" + - "ee/app/views/jira_connect/subscriptions/index.html.haml" + - "ee/app/views/layouts/jira_connect.html.haml" + - "ee/app/views/layouts/nav/ee/_epic_link.html.haml" + - "ee/app/views/layouts/nav/ee/admin/_new_monitoring_sidebar.html.haml" + - "ee/app/views/layouts/service_desk.html.haml" + - "ee/app/views/ldap_group_links/_form.html.haml" + - "ee/app/views/ldap_group_links/_ldap_group_link.html.haml" + - "ee/app/views/ldap_group_links/_ldap_group_links.html.haml" + - "ee/app/views/ldap_group_links/_ldap_group_links_show.html.haml" + - "ee/app/views/ldap_group_links/_ldap_group_links_synchronizations.html.haml" + - "ee/app/views/namespaces/_shared_runner_status.html.haml" + - "ee/app/views/namespaces/_shared_runners_minutes_setting.html.haml" + - "ee/app/views/namespaces/pipelines_quota/_extra_shared_runners_minutes_quota.html.haml" + - "ee/app/views/namespaces/pipelines_quota/_list.haml" + - "ee/app/views/notify/approved_merge_request_email.html.haml" + - "ee/app/views/notify/epic_status_changed_email.html.haml" + - "ee/app/views/notify/issues_csv_email.html.haml" + - "ee/app/views/notify/new_review_email.html.haml" + - "ee/app/views/notify/prometheus_alert_fired_email.html.haml" + - "ee/app/views/notify/send_admin_notification.html.haml" + - "ee/app/views/notify/send_unsubscribed_notification.html.haml" + - "ee/app/views/notify/unapproved_merge_request_email.html.haml" + - "ee/app/views/oauth/geo_auth/error.html.haml" + - "ee/app/views/profiles/pipeline_quota/index.haml" + - "ee/app/views/projects/audit_events/index.html.haml" + - "ee/app/views/projects/blob/_owners.html.haml" + - "ee/app/views/projects/commits/_mirror_status.html.haml" + - "ee/app/views/projects/feature_flags/_configure_feature_flags_modal.html.haml" + - "ee/app/views/projects/issues/_issue_weight.html.haml" + - "ee/app/views/projects/issues/_related_issues.html.haml" + - "ee/app/views/projects/issues/export_csv/_modal.html.haml" + - "ee/app/views/projects/jobs/_shared_runner_limit_warning.html.haml" + - "ee/app/views/projects/merge_requests/_approvals_count.html.haml" + - "ee/app/views/projects/merge_requests/widget/open/_geo.html.haml" + - "ee/app/views/projects/mirrors/_mirrored_repositories_count.html.haml" + - "ee/app/views/projects/protected_branches/ee/_create_protected_branch.html.haml" + - "ee/app/views/projects/protected_branches/ee/_dropdown.html.haml" + - "ee/app/views/projects/protected_branches/ee/_fallback_update_protected_branch.html.haml" + - "ee/app/views/projects/protected_tags/_protected_tag_extra_create_access_levels.haml" + - "ee/app/views/projects/protected_tags/ee/_create_protected_tag.html.haml" + - "ee/app/views/projects/push_rules/_index.html.haml" + - "ee/app/views/projects/services/gitlab_slack_application/_help.html.haml" + - "ee/app/views/projects/services/gitlab_slack_application/_slack_integration_form.html.haml" + - "ee/app/views/projects/services/prometheus/_metrics.html.haml" + - "ee/app/views/projects/settings/slacks/edit.html.haml" + - "ee/app/views/shared/_additional_email_text.html.haml" + - "ee/app/views/shared/_geo_info_modal.html.haml" + - "ee/app/views/shared/_mirror_update_button.html.haml" + - "ee/app/views/shared/_shared_runners_minutes_limit.html.haml" + - "ee/app/views/shared/audit_events/_event_table.html.haml" + - "ee/app/views/shared/boards/components/_list_weight.html.haml" + - "ee/app/views/shared/boards/components/sidebar/_epic.html.haml" + - "ee/app/views/shared/ee/_import_form.html.haml" + - "ee/app/views/shared/epic/_search_bar.html.haml" + - "ee/app/views/shared/issuable/_approvals.html.haml" + - "ee/app/views/shared/issuable/_board_create_list_dropdown.html.haml" + - "ee/app/views/shared/issuable/_filter_weight.html.haml" + - "ee/app/views/shared/issuable/_sidebar_item_epic.haml" + - "ee/app/views/shared/members/ee/_ldap_tag.html.haml" + - "ee/app/views/shared/members/ee/_override_member_buttons.html.haml" + - "ee/app/views/shared/members/ee/_sso_badge.html.haml" + - "ee/app/views/shared/milestones/_burndown.html.haml" + - "ee/app/views/shared/milestones/_weight.html.haml" + - "ee/app/views/shared/promotions/_promote_audit_events.html.haml" + - "ee/app/views/shared/promotions/_promote_burndown_charts.html.haml" + - "ee/app/views/shared/promotions/_promote_csv_export.html.haml" + - "ee/app/views/shared/promotions/_promote_issue_weights.html.haml" + - "ee/app/views/shared/promotions/_promote_repository_features.html.haml" + - "ee/app/views/shared/promotions/_promote_servicedesk.html.haml" + - "ee/app/views/shared/push_rules/_form.html.haml" + - "ee/app/views/unsubscribes/show.html.haml" + - "ee/app/views/admin/users/_auditor_access_level_radio.html.haml" + - "ee/app/views/admin/users/_auditor_user_badge.html.haml" + - "ee/app/views/projects/protected_branches/_update_protected_branch.html.haml" @@ -23,7 +23,7 @@ gem 'grape-path-helpers', '~> 1.1' gem 'faraday', '~> 0.12' # Authentication libraries -gem 'devise', '~> 4.4' +gem 'devise', '~> 4.6' gem 'doorkeeper', '~> 4.3' gem 'doorkeeper-openid_connect', '~> 1.5' gem 'omniauth', '~> 1.8' diff --git a/Gemfile.lock b/Gemfile.lock index be722b89a40..2dbff1674df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -158,7 +158,7 @@ GEM descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) device_detector (1.0.0) - devise (4.4.3) + devise (4.6.2) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0, < 6.0) @@ -1035,7 +1035,7 @@ DEPENDENCIES database_cleaner (~> 1.7.0) deckar01-task_list (= 2.2.0) device_detector - devise (~> 4.4) + devise (~> 4.6) devise-two-factor (~> 3.0.0) diffy (~> 3.1.0) discordrb-webhooks-blackst0ne (~> 3.3) diff --git a/app/assets/javascripts/batch_comments/mixins/resolved_status.js b/app/assets/javascripts/batch_comments/mixins/resolved_status.js index 20c31d9f8a4..96ee9f62ba4 100644 --- a/app/assets/javascripts/batch_comments/mixins/resolved_status.js +++ b/app/assets/javascripts/batch_comments/mixins/resolved_status.js @@ -1,7 +1,7 @@ export default { computed: { resolveButtonTitle() { - let title = 'Mark as resolved'; + let title = 'Mark comment as resolved'; if (this.resolvedBy) { title = `Resolved by ${this.resolvedBy.name}`; diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js index 02aa507ba03..8f5cece0788 100644 --- a/app/assets/javascripts/create_merge_request_dropdown.js +++ b/app/assets/javascripts/create_merge_request_dropdown.js @@ -118,7 +118,7 @@ export default class CreateMergeRequestDropdown { this.branchCreated = true; window.location.href = data.url; }) - .catch(() => Flash('Failed to create a branch for this issue. Please try again.')); + .catch(() => Flash(__('Failed to create a branch for this issue. Please try again.'))); } createMergeRequest() { @@ -130,7 +130,7 @@ export default class CreateMergeRequestDropdown { this.mergeRequestCreated = true; window.location.href = data.url; }) - .catch(() => Flash('Failed to create Merge Request. Please try again.')); + .catch(() => Flash(__('Failed to create Merge Request. Please try again.'))); } disable() { @@ -227,7 +227,7 @@ export default class CreateMergeRequestDropdown { .catch(() => { this.unavailable(); this.disable(); - new Flash('Failed to get ref.'); + new Flash(__('Failed to get ref.')); this.isGettingRef = false; diff --git a/app/assets/javascripts/dirty_submit/dirty_submit_form.js b/app/assets/javascripts/dirty_submit/dirty_submit_form.js index 765969daa32..0fcaec9531c 100644 --- a/app/assets/javascripts/dirty_submit/dirty_submit_form.js +++ b/app/assets/javascripts/dirty_submit/dirty_submit_form.js @@ -21,10 +21,15 @@ class DirtySubmitForm { } registerListeners() { - const throttledUpdateDirtyInput = _.throttle( - event => this.updateDirtyInput(event), - DirtySubmitForm.THROTTLE_DURATION, + const getThrottledHandlerForInput = _.memoize(() => + _.throttle(event => this.updateDirtyInput(event), DirtySubmitForm.THROTTLE_DURATION), ); + + const throttledUpdateDirtyInput = event => { + const throttledHandler = getThrottledHandlerForInput(event.target.name); + throttledHandler(event); + }; + this.form.addEventListener('input', throttledUpdateDirtyInput); this.form.addEventListener('change', throttledUpdateDirtyInput); $(this.form).on('change.select2', throttledUpdateDirtyInput); diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index 9987fbcb6a7..0ff26445a6a 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -4,6 +4,7 @@ import _ from 'underscore'; import './behaviors/preview_markdown'; import csrf from './lib/utils/csrf'; import axios from './lib/utils/axios_utils'; +import { n__, __ } from '~/locale'; Dropzone.autoDiscover = false; @@ -90,7 +91,7 @@ export default function dropzoneInput(form) { if (!processingFileCount) $attachButton.removeClass('hide'); addFileToForm(response.link.url); }, - error: (file, errorMessage = 'Attaching the file failed.', xhr) => { + error: (file, errorMessage = __('Attaching the file failed.'), xhr) => { // If 'error' event is fired by dropzone, the second parameter is error message. // If the 'errorMessage' parameter is empty, the default error message is set. // If the 'error' event is fired by backend (xhr) error response, the third parameter is @@ -273,19 +274,11 @@ export default function dropzoneInput(form) { }; updateAttachingMessage = (files, messageContainer) => { - let attachingMessage; const filesCount = files.filter(file => file.status === 'uploading' || file.status === 'queued') .length; + const attachingMessage = n__('Attaching a file', 'Attaching %d files', filesCount); - // Dinamycally change uploading files text depending on files number in - // dropzone files queue. - if (filesCount > 1) { - attachingMessage = `Attaching ${filesCount} files -`; - } else { - attachingMessage = 'Attaching a file -'; - } - - messageContainer.text(attachingMessage); + messageContainer.text(`${attachingMessage} -`); }; form.find('.markdown-selector').click(function onMarkdownClick(e) { diff --git a/app/assets/javascripts/import_projects/store/getters.js b/app/assets/javascripts/import_projects/store/getters.js index f03474a8404..727b80765bd 100644 --- a/app/assets/javascripts/import_projects/store/getters.js +++ b/app/assets/javascripts/import_projects/store/getters.js @@ -1,3 +1,5 @@ +import { __ } from '~/locale'; + export const namespaceSelectOptions = state => { const serializedNamespaces = state.namespaces.map(({ fullPath }) => ({ id: fullPath, @@ -5,9 +7,9 @@ export const namespaceSelectOptions = state => { })); return [ - { text: 'Groups', children: serializedNamespaces }, + { text: __('Groups'), children: serializedNamespaces }, { - text: 'Users', + text: __('Users'), children: [{ id: state.defaultTargetNamespace, text: state.defaultTargetNamespace }], }, ]; diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js index 4d304c5fe69..c6dd21cd2d4 100644 --- a/app/assets/javascripts/label_manager.js +++ b/app/assets/javascripts/label_manager.js @@ -5,13 +5,14 @@ import Sortable from 'sortablejs'; import flash from './flash'; import axios from './lib/utils/axios_utils'; +import { __ } from './locale'; export default class LabelManager { constructor({ togglePriorityButton, prioritizedLabels, otherLabels } = {}) { this.togglePriorityButton = togglePriorityButton || $('.js-toggle-priority'); this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels'); this.otherLabels = otherLabels || $('.js-other-labels'); - this.errorMessage = 'Unable to update label prioritization at this time'; + this.errorMessage = __('Unable to update label prioritization at this time'); this.emptyState = document.querySelector('#js-priority-labels-empty-state'); this.$badgeItemTemplate = $('#js-badge-item-template'); diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 2c30b4ea587..3f954b43ee3 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -4,7 +4,7 @@ import $ from 'jquery'; import _ from 'underscore'; -import { sprintf, __ } from './locale'; +import { sprintf, s__, __ } from './locale'; import axios from './lib/utils/axios_utils'; import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; import CreateLabelDropdown from './create_label'; @@ -178,7 +178,7 @@ export default class LabelsSelect { }); } } else { - template = '<span class="no-value">None</span>'; + template = `<span class="no-value">${__('None')}</span>`; } $value.removeAttr('style').html(template); $sidebarCollapsedValue.text(labelCount); @@ -190,7 +190,9 @@ export default class LabelsSelect { if (labelTitles.length > 5) { labelTitles = labelTitles.slice(0, 5); - labelTitles.push('and ' + (data.labels.length - 5) + ' more'); + labelTitles.push( + sprintf(s__('Labels|and %{count} more'), { count: data.labels.length - 5 }), + ); } labelTooltipTitle = labelTitles.join(', '); @@ -219,13 +221,13 @@ export default class LabelsSelect { if (showNo) { extraData.unshift({ id: 0, - title: 'No Label', + title: __('No Label'), }); } if (showAny) { extraData.unshift({ isAny: true, - title: 'Any Label', + title: __('Any Label'), }); } if (extraData.length) { @@ -341,7 +343,7 @@ export default class LabelsSelect { if (selected && selected.id === 0) { this.selected = []; - return 'No Label'; + return __('No Label'); } else if (isSelected) { this.selected.push(title); } else if (!isSelected && title) { @@ -579,7 +581,7 @@ export default class LabelsSelect { if ($('.selected-issuable:checked').length) { return; } - return $('.issues-bulk-update .labels-filter .dropdown-toggle-text').text('Label'); + return $('.issues-bulk-update .labels-filter .dropdown-toggle-text').text(__('Label')); } // eslint-disable-next-line class-methods-use-this enableBulkLabelDropdown() { diff --git a/app/assets/javascripts/locale/index.js b/app/assets/javascripts/locale/index.js index 1ae3362c4bc..41aa0f4ddb9 100644 --- a/app/assets/javascripts/locale/index.js +++ b/app/assets/javascripts/locale/index.js @@ -11,7 +11,7 @@ delete window.translations; @param text The text to be translated @returns {String} The translated text */ -const gettext = text => locale.gettext.bind(locale)(ensureSingleLine(text)); +const gettext = text => locale.gettext(ensureSingleLine(text)); /** Translate the text with a number diff --git a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue index a79ef07f1c5..c563514d36b 100644 --- a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue +++ b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue @@ -33,8 +33,7 @@ export default { text() { return sprintf( s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}. - Existing project milestones with the same title will be merged. - This action cannot be reversed.`), + Existing project milestones with the same title will be merged.`), { milestoneTitle: this.milestoneTitle, groupName: this.groupName }, ); }, @@ -72,6 +71,9 @@ export default { <template slot="title"> {{ title }} </template> - {{ text }} + <div> + <p>{{ text }}</p> + <p>{{ s__('Milestones|This action cannot be reversed.') }}</p> + </div> </gl-modal> </template> diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js index d3c604dcee1..5395e14cc79 100644 --- a/app/assets/javascripts/project_label_subscription.js +++ b/app/assets/javascripts/project_label_subscription.js @@ -38,9 +38,9 @@ export default class ProjectLabelSubscription { let newAction; if (oldStatus === 'unsubscribed') { - [newStatus, newAction] = ['subscribed', 'Unsubscribe']; + [newStatus, newAction] = ['subscribed', __('Unsubscribe')]; } else { - [newStatus, newAction] = ['unsubscribed', 'Subscribe']; + [newStatus, newAction] = ['unsubscribed', __('Subscribe')]; } $btn.removeClass('disabled'); diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js index 5ee510eb11d..dbe354a547b 100644 --- a/app/assets/javascripts/project_select.js +++ b/app/assets/javascripts/project_select.js @@ -3,6 +3,7 @@ import $ from 'jquery'; import Api from './api'; import ProjectSelectComboButton from './project_select_combo_button'; +import { s__ } from './locale'; export default function projectSelect() { import(/* webpackChunkName: 'select2' */ 'select2/select2') @@ -21,9 +22,9 @@ export default function projectSelect() { this.includeProjectsInSubgroups = $(select).data('includeProjectsInSubgroups') || false; this.allowClear = $(select).data('allowClear') || false; - placeholder = 'Search for project'; + placeholder = s__('ProjectSelect|Search for project'); if (this.includeGroups) { - placeholder += ' or group'; + placeholder += s__('ProjectSelect| or group'); } $(select).select2({ diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue index 420e71f5e86..241185e3126 100644 --- a/app/assets/javascripts/reports/components/report_section.vue +++ b/app/assets/javascripts/reports/components/report_section.vue @@ -156,7 +156,7 @@ export default { <button v-if="isCollapsible" type="button" - class="js-collapse-btn btn float-right btn-sm" + class="js-collapse-btn btn float-right btn-sm qa-expand-report-button" @click="toggleCollapsed" > {{ collapseText }} diff --git a/app/assets/javascripts/repository/components/table/header.vue b/app/assets/javascripts/repository/components/table/header.vue new file mode 100644 index 00000000000..9d30aa88155 --- /dev/null +++ b/app/assets/javascripts/repository/components/table/header.vue @@ -0,0 +1,9 @@ +<template> + <thead> + <tr> + <th id="name" scope="col">{{ s__('ProjectFileTree|Name') }}</th> + <th id="last-commit" scope="col" class="d-none d-sm-table-cell">{{ __('Last commit') }}</th> + <th id="last-update" scope="col" class="text-right">{{ __('Last update') }}</th> + </tr> + </thead> +</template> diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue new file mode 100644 index 00000000000..7119861c7b3 --- /dev/null +++ b/app/assets/javascripts/repository/components/table/index.vue @@ -0,0 +1,65 @@ +<script> +import { GlLoadingIcon } from '@gitlab/ui'; +import { sprintf, __ } from '../../../locale'; +import getRefMixin from '../../mixins/get_ref'; +import getFiles from '../../queries/getFiles.graphql'; +import TableHeader from './header.vue'; + +export default { + components: { + GlLoadingIcon, + TableHeader, + }, + mixins: [getRefMixin], + apollo: { + files: { + query: getFiles, + variables() { + return { + ref: this.ref, + path: this.path, + }; + }, + }, + }, + props: { + path: { + type: String, + required: true, + }, + }, + data() { + return { + files: [], + }; + }, + computed: { + tableCaption() { + return sprintf( + __('Files, directories, and submodules in the path %{path} for commit reference %{ref}'), + { path: this.path, ref: this.ref }, + ); + }, + isLoadingFiles() { + return this.$apollo.queries.files.loading; + }, + }, +}; +</script> + +<template> + <div class="tree-content-holder"> + <div class="table-holder bordered-box"> + <table class="table tree-table qa-file-tree" aria-live="polite"> + <caption class="sr-only"> + {{ + tableCaption + }} + </caption> + <table-header /> + <tbody></tbody> + </table> + <gl-loading-icon v-if="isLoadingFiles" class="my-3" size="md" /> + </div> + </div> +</template> diff --git a/app/assets/javascripts/repository/graphql.js b/app/assets/javascripts/repository/graphql.js index febfcce780c..0aedc73fc12 100644 --- a/app/assets/javascripts/repository/graphql.js +++ b/app/assets/javascripts/repository/graphql.js @@ -4,7 +4,13 @@ import createDefaultClient from '~/lib/graphql'; Vue.use(VueApollo); -const defaultClient = createDefaultClient({}); +const defaultClient = createDefaultClient({ + Query: { + files() { + return []; + }, + }, +}); export default new VueApollo({ defaultClient, diff --git a/app/assets/javascripts/repository/mixins/get_ref.js b/app/assets/javascripts/repository/mixins/get_ref.js new file mode 100644 index 00000000000..b06087d6f42 --- /dev/null +++ b/app/assets/javascripts/repository/mixins/get_ref.js @@ -0,0 +1,14 @@ +import getRef from '../queries/getRef.graphql'; + +export default { + apollo: { + ref: { + query: getRef, + }, + }, + data() { + return { + ref: '', + }; + }, +}; diff --git a/app/assets/javascripts/repository/pages/index.vue b/app/assets/javascripts/repository/pages/index.vue index fdbf195f0f6..2d92e9174ca 100644 --- a/app/assets/javascripts/repository/pages/index.vue +++ b/app/assets/javascripts/repository/pages/index.vue @@ -1,11 +1,9 @@ <script> -import getRef from '../queries/getRef.graphql'; +import FileTable from '../components/table/index.vue'; export default { - apollo: { - ref: { - query: getRef, - }, + components: { + FileTable, }, data() { return { @@ -16,9 +14,5 @@ export default { </script> <template> - <div> - <router-link :to="{ path: `/tree/${ref}/app` }"> - Go to tree - </router-link> - </div> + <file-table path="/" /> </template> diff --git a/app/assets/javascripts/repository/pages/tree.vue b/app/assets/javascripts/repository/pages/tree.vue index f51aafee775..413102b2cd3 100644 --- a/app/assets/javascripts/repository/pages/tree.vue +++ b/app/assets/javascripts/repository/pages/tree.vue @@ -1,5 +1,10 @@ <script> +import FileTable from '../components/table/index.vue'; + export default { + component: { + FileTable, + }, props: { path: { type: String, @@ -11,5 +16,5 @@ export default { </script> <template> - <div>{{ path }}</div> + <file-table :path="path" /> </template> diff --git a/app/assets/javascripts/repository/queries/getFiles.graphql b/app/assets/javascripts/repository/queries/getFiles.graphql new file mode 100644 index 00000000000..5ceaf67ea82 --- /dev/null +++ b/app/assets/javascripts/repository/queries/getFiles.graphql @@ -0,0 +1,8 @@ +query getFiles($path: String!, $ref: String!) { + files(path: $path, ref: $ref) @client { + id + name + fullPath + type + } +} diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index 9a0cdc02952..72e061df573 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -5,7 +5,7 @@ import _ from 'underscore'; import Cookies from 'js-cookie'; import flash from './flash'; import axios from './lib/utils/axios_utils'; -import { __ } from './locale'; +import { sprintf, s__, __ } from './locale'; function Sidebar(currentUser) { this.toggleTodo = this.toggleTodo.bind(this); @@ -101,7 +101,10 @@ Sidebar.prototype.toggleTodo = function(e) { this.todoUpdateDone(data); }) .catch(() => - flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`), + flash(sprintf(__('There was an error %{message} todo.')), { + message: + ajaxType === 'post' ? s__('RightSidebar|adding a') : s__('RightSidebar|deleting the'), + }), ); }; diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss index 3d5208c3db5..e8176e59c19 100644 --- a/app/assets/stylesheets/framework/timeline.scss +++ b/app/assets/stylesheets/framework/timeline.scss @@ -55,4 +55,5 @@ .discussion .timeline-entry { margin: 0; border-right: 0; + border-radius: $border-radius-default $border-radius-default 0 0; } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index f2b67a693c3..fcb57db590a 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -107,6 +107,7 @@ $note-form-margin-left: 72px; &.collapsed { color: $gl-text-color-secondary; + border-radius: 0 0 $border-radius-default $border-radius-default; svg { float: left; diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 52b6e828cfa..50e9418677c 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -346,7 +346,7 @@ class IssuableFinder def attempt_project_search_optimizations? params[:attempt_project_search_optimizations] && - Feature.enabled?(:attempt_project_search_optimizations) + Feature.enabled?(:attempt_project_search_optimizations, default_enabled: true) end def count_key(value) diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index a2d615ee732..530aecc2bf9 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -8,14 +8,16 @@ module Types expose_permissions Types::PermissionTypes::Group - field :web_url, GraphQL::STRING_TYPE, null: true + field :web_url, GraphQL::STRING_TYPE, null: false field :avatar_url, GraphQL::STRING_TYPE, null: true, resolve: -> (group, args, ctx) do group.avatar_url(only_path: false) end if ::Group.supports_nested_objects? - field :parent, GroupType, null: true + field :parent, GroupType, + null: true, + resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.parent_id).find } end end end diff --git a/app/helpers/groups/group_members_helper.rb b/app/helpers/groups/group_members_helper.rb new file mode 100644 index 00000000000..a5d2f76820f --- /dev/null +++ b/app/helpers/groups/group_members_helper.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Groups::GroupMembersHelper + def group_member_select_options + { multiple: true, class: 'input-clamp', scope: :all, email_user: true } + end +end diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index ceecd931bba..01711e477b1 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -3,7 +3,7 @@ module Clusters module Applications class Runner < ApplicationRecord - VERSION = '0.4.1'.freeze + VERSION = '0.5.0'.freeze self.table_name = 'clusters_applications_runners' diff --git a/app/models/user.rb b/app/models/user.rb index 60f69659a6b..2eb5c63a4cc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1497,15 +1497,6 @@ class User < ApplicationRecord devise_mailer.__send__(notification, self, *args).deliver_later # rubocop:disable GitlabSecurity/PublicSend end - # This works around a bug in Devise 4.2.0 that erroneously causes a user to - # be considered active in MySQL specs due to a sub-second comparison - # issue. For more details, see: https://gitlab.com/gitlab-org/gitlab-ee/issues/2362#note_29004709 - def confirmation_period_valid? - return false if self.class.allow_unconfirmed_access_for == 0.days - - super - end - def ensure_user_rights_and_limits if external? self.can_create_group = false diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml index 9ef58faf8cc..eb4dfdf2858 100644 --- a/app/views/admin/broadcast_messages/index.html.haml +++ b/app/views/admin/broadcast_messages/index.html.haml @@ -32,7 +32,7 @@ %td = message.ends_at %td - = link_to icon('pencil-square-o'), edit_admin_broadcast_message_path(message), title: 'Edit', class: 'btn btn-sm' - = link_to icon('times'), admin_broadcast_message_path(message), method: :delete, remote: true, title: 'Remove', class: 'js-remove-tr btn btn-sm btn-danger' + = link_to sprite_icon('pencil-square'), edit_admin_broadcast_message_path(message), title: 'Edit', class: 'btn' + = link_to sprite_icon('remove'), admin_broadcast_message_path(message), method: :delete, remote: true, title: 'Remove', class: 'js-remove-tr btn btn-danger' = paginate @broadcast_messages, theme: 'gitlab' diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml index b4930b41c09..89bd7b31352 100644 --- a/app/views/ci/variables/_variable_row.html.haml +++ b/app/views/ci/variables/_variable_row.html.haml @@ -23,7 +23,7 @@ .ci-variable-row-body.border-bottom %input.js-ci-variable-input-id{ type: "hidden", name: id_input_name, value: id } %input.js-ci-variable-input-destroy{ type: "hidden", name: destroy_input_name } - %select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control.table-section.section-15{ name: variable_type_input_name } + %select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control.custom-select.table-section.section-15{ name: variable_type_input_name } = options_for_select(ci_variable_type_options, variable_type) %input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control.table-section.section-15{ type: "text", name: key_input_name, diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml index 73e70dc63e5..f8aa3cf98dc 100644 --- a/app/views/devise/confirmations/new.html.haml +++ b/app/views/devise/confirmations/new.html.haml @@ -3,7 +3,7 @@ .login-body = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f| .devise-errors - = devise_error_messages! + = render "devise/shared/error_messages", resource: resource .form-group = f.label :email = f.email_field :email, class: "form-control", required: true, title: 'Please provide a valid email address.' diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index dd1edb5fdc9..09ea7716a47 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -3,7 +3,7 @@ .login-body = form_for(resource, as: resource_name, url: password_path(:user), html: { method: :put, class: 'gl-show-field-errors' }) do |f| .devise-errors - = devise_error_messages! + = render "devise/shared/error_messages", resource: resource = f.hidden_field :reset_password_token .form-group = f.label 'New password', for: "user_password" diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml index 99ce13adf74..fe999851605 100644 --- a/app/views/devise/passwords/new.html.haml +++ b/app/views/devise/passwords/new.html.haml @@ -3,7 +3,7 @@ .login-body = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f| .devise-errors - = devise_error_messages! + = render "devise/shared/error_messages", resource: resource .form-group = f.label :email = f.email_field :email, class: "form-control", required: true, value: params[:user_email], autofocus: true, title: 'Please provide a valid email address.' diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index f379e71ae5b..5a1388ac7a1 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -1,7 +1,7 @@ <h2>Edit <%= resource_name.to_s.humanize %></h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> - <%= devise_error_messages! %> + <%= render "devise/shared/error_messages", resource: resource %> <div><%= f.label :email %><br /> <%= f.email_field :email %></div> diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 427db070253..a7434059de4 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -2,7 +2,7 @@ .login-body = form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f| .devise-errors - = devise_error_messages! + = render "devise/shared/error_messages", resource: resource .name.form-group = f.label :name, 'Full name', class: 'label-bold' = f.text_field :name, class: "form-control top qa-new-user-name js-block-emoji", required: true, title: _("This field is required.") diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml index b2f48a4e0bf..1167f1718d6 100644 --- a/app/views/devise/unlocks/new.html.haml +++ b/app/views/devise/unlocks/new.html.haml @@ -3,7 +3,7 @@ .login-body = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f| .devise-errors - = devise_error_messages! + = render "devise/shared/error_messages", resource: resource .form-group.append-bottom-20 = f.label :email = f.email_field :email, class: 'form-control', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', title: 'Please provide a valid email address.' diff --git a/app/views/groups/group_members/_new_group_member.html.haml b/app/views/groups/group_members/_new_group_member.html.haml index c8cdc2cc3e4..8b511f6866f 100644 --- a/app/views/groups/group_members/_new_group_member.html.haml +++ b/app/views/groups/group_members/_new_group_member.html.haml @@ -1,7 +1,7 @@ = form_for @group_member, url: group_group_members_path(@group), html: { class: 'users-project-form users-group-form' } do |f| .row .col-md-4.col-lg-6 - = users_select_tag(:user_ids, multiple: true, class: 'input-clamp', scope: :all, email_user: true) + = users_select_tag(:user_ids, group_member_select_options) .form-text.text-muted.append-bottom-10 Search for members by name, username, or email, or invite new ones using their email address. diff --git a/app/views/import/github/new.html.haml b/app/views/import/github/new.html.haml index cf32c5c9387..72e5934574a 100644 --- a/app/views/import/github/new.html.haml +++ b/app/views/import/github/new.html.haml @@ -22,6 +22,8 @@ = text_field_tag :personal_access_token, '', class: 'form-control append-right-8', placeholder: _('Personal Access Token'), size: 40 = submit_tag _('List your GitHub repositories'), class: 'btn btn-success' + = render_if_exists 'import/github/ci_cd_only' + - unless github_import_configured? %hr %p diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 0e15f581ddc..1074cd6bf4e 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -22,6 +22,8 @@ %span.badge.badge-success.prepend-left-5 = s_('Branches|protected') + = render_if_exists 'projects/branches/diverged_from_upstream' + .block-truncated - if commit = render 'projects/branches/commit', commit: commit, project: @project diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml index 68f74f702ea..590fcdb0234 100644 --- a/app/views/projects/diffs/_content.html.haml +++ b/app/views/projects/diffs/_content.html.haml @@ -1,2 +1,2 @@ .diff-content - = render 'projects/diffs/viewer', viewer: diff_file.rich_viewer || diff_file.simple_viewer + = render 'projects/diffs/viewer', viewer: diff_file.viewer diff --git a/app/views/shared/_mini_pipeline_graph.html.haml b/app/views/shared/_mini_pipeline_graph.html.haml index b46479d9f1a..a1f21c2a83e 100644 --- a/app/views/shared/_mini_pipeline_graph.html.haml +++ b/app/views/shared/_mini_pipeline_graph.html.haml @@ -13,5 +13,5 @@ %ul %li.js-builds-dropdown-loading.hidden - .text-center - %i.fa.fa-spinner.fa-spin{ 'aria-hidden': 'true', 'aria-label': 'Loading' } + .loading-container.text-center + %span.spinner{ 'aria-label': 'Loading' } diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index edaeff782de..43503e1d08a 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -4,10 +4,7 @@ - group = local_assigns[:group] - is_dynamic_milestone = milestone.legacy_group_milestone? || milestone.dashboard_milestone? -.detail-page-header - %a.btn.btn-default.btn-grouped.float-right.d-block.d-sm-none.js-sidebar-toggle{ href: "#" } - = icon('angle-double-left') - +.detail-page-header.milestone-page-header .status-box{ class: "status-box-#{milestone.closed? ? 'closed' : 'open'}" } - if milestone.closed? Closed @@ -15,14 +12,17 @@ Expired - else Open - %span.identifier - Milestone #{milestone.title} - - if milestone.due_date || milestone.start_date - %span.creator - · - = milestone_date_range(milestone) - - if group - .float-right + + .header-text-content + %span.identifier + Milestone #{milestone.title} + - if milestone.due_date || milestone.start_date + %span.creator + · + = milestone_date_range(milestone) + + .milestone-buttons + - if group - if can?(current_user, :admin_milestone, group) - if milestone.group_milestone? = link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do @@ -35,6 +35,9 @@ - unless is_dynamic_milestone = render 'shared/milestones/delete_button' + %a.btn.btn-default.btn-grouped.float-right.d-block.d-sm-none.js-sidebar-toggle{ href: "#" } + = icon('angle-double-left') + = render 'shared/milestones/deprecation_message' if is_dynamic_milestone .detail-page-description.milestone-detail diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb index adc38226405..8e2a18a8fd8 100644 --- a/app/workers/build_finished_worker.rb +++ b/app/workers/build_finished_worker.rb @@ -30,6 +30,7 @@ class BuildFinishedWorker # We execute these async as these are independent operations. BuildHooksWorker.perform_async(build.id) ArchiveTraceWorker.perform_async(build.id) + ExpirePipelineCacheWorker.perform_async(build.pipeline_id) ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat? end end diff --git a/changelogs/unreleased/39304-broadcast-message-buttons.yml b/changelogs/unreleased/39304-broadcast-message-buttons.yml new file mode 100644 index 00000000000..7eb289fca1f --- /dev/null +++ b/changelogs/unreleased/39304-broadcast-message-buttons.yml @@ -0,0 +1,5 @@ +--- +title: Update broadcast message action icons +merge_request: 28496 +author: Jarek Ostrowski @jareko +type: fixed diff --git a/changelogs/unreleased/55127-add-delay-after-mr-creation-for-async-tasks-to-complete.yml b/changelogs/unreleased/55127-add-delay-after-mr-creation-for-async-tasks-to-complete.yml deleted file mode 100644 index ac3bb596842..00000000000 --- a/changelogs/unreleased/55127-add-delay-after-mr-creation-for-async-tasks-to-complete.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Wait for pipeline creation to complete before accepting a MR via API -merge_request: 27978 -author: kerrizor -type: fixed diff --git a/changelogs/unreleased/58538-discussion-top-radius.yml b/changelogs/unreleased/58538-discussion-top-radius.yml new file mode 100644 index 00000000000..0fb16055623 --- /dev/null +++ b/changelogs/unreleased/58538-discussion-top-radius.yml @@ -0,0 +1,5 @@ +--- +title: Fix border radius of discussions +merge_request: 28490 +author: +type: fixed diff --git a/changelogs/unreleased/60750-milestone-header.yml b/changelogs/unreleased/60750-milestone-header.yml new file mode 100644 index 00000000000..62cfdaf6ea7 --- /dev/null +++ b/changelogs/unreleased/60750-milestone-header.yml @@ -0,0 +1,5 @@ +--- +title: Fix layout of group milestone header +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/61928-remove-throttle-from-dirty-submit.yml b/changelogs/unreleased/61928-remove-throttle-from-dirty-submit.yml new file mode 100644 index 00000000000..f8ef5dbb53b --- /dev/null +++ b/changelogs/unreleased/61928-remove-throttle-from-dirty-submit.yml @@ -0,0 +1,6 @@ +--- +title: Fix issue that causes "Save changes" button in project settings pages to be + enabled/disabled incorrectly when changes are made to the form +merge_request: 28377 +author: +type: fixed diff --git a/changelogs/unreleased/61990-spinner.yml b/changelogs/unreleased/61990-spinner.yml new file mode 100644 index 00000000000..27d35e56cc9 --- /dev/null +++ b/changelogs/unreleased/61990-spinner.yml @@ -0,0 +1,5 @@ +--- +title: Updates loading icon in commits page +merge_request: 28475 +author: +type: fixed diff --git a/changelogs/unreleased/62038-chevron-down.yml b/changelogs/unreleased/62038-chevron-down.yml new file mode 100644 index 00000000000..9d58c364bc9 --- /dev/null +++ b/changelogs/unreleased/62038-chevron-down.yml @@ -0,0 +1,5 @@ +--- +title: Adds arrow icons to select option in CI/CD settings +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/add-allow_failure-to-job-api.yml b/changelogs/unreleased/add-allow_failure-to-job-api.yml new file mode 100644 index 00000000000..5dd2b9708c5 --- /dev/null +++ b/changelogs/unreleased/add-allow_failure-to-job-api.yml @@ -0,0 +1,5 @@ +--- +title: Add allow_failure attribute to Job API +merge_request: 28406 +author: +type: added diff --git a/changelogs/unreleased/dz-scope-project-routes.yml b/changelogs/unreleased/dz-scope-project-routes.yml new file mode 100644 index 00000000000..66eb5d928f0 --- /dev/null +++ b/changelogs/unreleased/dz-scope-project-routes.yml @@ -0,0 +1,5 @@ +--- +title: Move some project routes under /-/ scope +merge_request: 28435 +author: +type: changed diff --git a/changelogs/unreleased/mark-comment-resolved.yml b/changelogs/unreleased/mark-comment-resolved.yml new file mode 100644 index 00000000000..3343d18d16f --- /dev/null +++ b/changelogs/unreleased/mark-comment-resolved.yml @@ -0,0 +1,5 @@ +--- +title: Change resolve button text to mark comment as resolved +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/patch-55.yml b/changelogs/unreleased/patch-55.yml new file mode 100644 index 00000000000..7a6d6bfed2f --- /dev/null +++ b/changelogs/unreleased/patch-55.yml @@ -0,0 +1,5 @@ +--- +title: Better isolated `Docker.gitlab-ci.yml` to avoid interference with other job configurations. +merge_request: 28213 +author: lrkwz +type: changed diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-5-0.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-5-0.yml new file mode 100644 index 00000000000..494331158fb --- /dev/null +++ b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-5-0.yml @@ -0,0 +1,5 @@ +--- +title: Update GitLab Runner Helm Chart to 0.5.0 +merge_request: 28497 +author: +type: other diff --git a/config/initializers/8_devise.rb b/config/initializers/8_devise.rb index 4683b02f300..3dd12c7e64d 100644 --- a/config/initializers/8_devise.rb +++ b/config/initializers/8_devise.rb @@ -100,6 +100,11 @@ Devise.setup do |config| # secure: true in order to force SSL only cookies. # config.cookie_options = {} + # When set to false, does not sign a user in automatically after their password is + # changed. Defaults to true, so a user is signed in automatically after a password + # is changed. + config.sign_in_after_change_password = false + # Send a notification email when the user's password is changed config.send_password_change_notification = true diff --git a/config/routes/project.rb b/config/routes/project.rb index 93d746f3282..fdc2a3c0086 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -442,16 +442,23 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end end - namespace :settings do - get :members, to: redirect("%{namespace_id}/%{project_id}/project_members") - resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do - post :reset_cache - put :reset_registration_token - end - resource :integrations, only: [:show] - resource :repository, only: [:show], controller: :repository do - post :create_deploy_token, path: 'deploy_token/create' - post :cleanup + + scope '-' do + namespace :settings do + get :members, to: redirect("%{namespace_id}/%{project_id}/project_members") + + resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do + post :reset_cache + put :reset_registration_token + end + + resource :operations, only: [:show, :update] + resource :integrations, only: [:show] + + resource :repository, only: [:show], controller: :repository do + post :create_deploy_token, path: 'deploy_token/create' + post :cleanup + end end end @@ -465,10 +472,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do # its preferable to keep it below all other project routes draw :wiki draw :repository - - namespace :settings do - resource :operations, only: [:show, :update] - end end resources(:projects, @@ -493,4 +496,18 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end end + + # Legacy routes. + # Introduced in 12.0. + # Should be removed after 12.1 + scope(path: '*namespace_id', + as: :namespace, + namespace_id: Gitlab::PathRegex.full_namespace_route_regex) do + scope(path: ':project_id', + constraints: { project_id: Gitlab::PathRegex.project_route_regex }, + module: :projects, + as: :project) do + Gitlab::Routing.redirect_legacy_paths(self, :settings, :branches, :tags, :network, :graphs) + end + end end diff --git a/config/routes/repository.rb b/config/routes/repository.rb index f5201b9ddbb..b96315bfe8b 100644 --- a/config/routes/repository.rb +++ b/config/routes/repository.rb @@ -39,7 +39,7 @@ scope format: false do end end - scope constraints: { id: Gitlab::PathRegex.git_reference_regex } do + scope path: '-', constraints: { id: Gitlab::PathRegex.git_reference_regex } do resources :network, only: [:show] resources :graphs, only: [:show] do diff --git a/doc/api/README.md b/doc/api/README.md index 7ec7955c596..439f58c1253 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -186,6 +186,8 @@ curl "https://gitlab.example.com/api/v4/projects" The API uses JSON to serialize data. You don't need to specify `.json` at the end of an API URL. +All of the API endpoints that use the `POST`, `PUT` or `PATCH` method support params in the request body, with `Content-Type` `application/x-www-form-urlencoded`, `multipart/form-data` or `application/json`. + ## Authentication Most API requests require authentication, or will only return public data when diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md index cf02bbd9c92..10e1ef0e533 100644 --- a/doc/api/graphql/index.md +++ b/doc/api/graphql/index.md @@ -16,6 +16,20 @@ added to the API without creating breaking changes. This allows us to have a versionless API as described in [the GraphQL documentation](https://graphql.org/learn/best-practices/#versioning). +## Vision + +We want the GraphQL API to be the **primary** means of interacting +programmatically with GitLab. To achieve this, it needs full coverage - anything +possible in the REST API should also be possible in the GraphQL API. + +To help us meet this vision, the frontend should use GraphQL in preference to +the REST API for new features, although the alpha status of GraphQL may prevent +this from being a possibility at times. + +There are no plans to deprecate the REST API. To reduce the technical burden of +supporting two APIs in parallel, they should share implementations as much as +possible. + ## Enabling the GraphQL feature The GraphQL API itself is currently in Alpha, and therefore hidden behind a @@ -32,7 +46,6 @@ curl --data "value=100" --header "PRIVATE-TOKEN: <your_access_token>" https://gi A first iteration of a GraphQL API includes the following queries 1. `project` : Within a project it is also possible to fetch a `mergeRequest` by IID. - 1. `group` : Only basic group information is currently supported. ## GraphiQL diff --git a/doc/api/jobs.md b/doc/api/jobs.md index 877cd99723a..d51451b9c83 100644 --- a/doc/api/jobs.md +++ b/doc/api/jobs.md @@ -32,6 +32,7 @@ Example of response "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "created_at": "2015-12-24T15:51:21.727Z", "started_at": "2015-12-24T17:54:24.729Z", "finished_at": "2015-12-24T17:54:24.921Z", @@ -81,6 +82,7 @@ Example of response "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "created_at": "2015-12-24T15:51:21.802Z", "started_at": "2015-12-24T17:54:27.722Z", "finished_at": "2015-12-24T17:54:27.895Z", @@ -165,6 +167,7 @@ Example of response "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "created_at": "2015-12-24T15:51:21.727Z", "started_at": "2015-12-24T17:54:24.729Z", "finished_at": "2015-12-24T17:54:24.921Z", @@ -214,6 +217,7 @@ Example of response "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "created_at": "2015-12-24T15:51:21.802Z", "started_at": "2015-12-24T17:54:27.722Z", "finished_at": "2015-12-24T17:54:27.895Z", @@ -296,6 +300,7 @@ Example of response "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "created_at": "2015-12-24T15:51:21.880Z", "started_at": "2015-12-24T17:54:30.733Z", "finished_at": "2015-12-24T17:54:31.198Z", @@ -528,6 +533,7 @@ Example of response "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "created_at": "2016-01-11T10:13:33.506Z", "started_at": "2016-01-11T10:14:09.526Z", "finished_at": null, @@ -576,6 +582,7 @@ Example of response "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "created_at": "2016-01-11T10:13:33.506Z", "started_at": null, "finished_at": null, @@ -628,6 +635,7 @@ Example of response "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "download_url": null, "id": 42, "name": "rubocop", @@ -681,6 +689,7 @@ Example response: "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "download_url": null, "id": 42, "name": "rubocop", @@ -757,6 +766,7 @@ Example of response "title": "Test the CI integration." }, "coverage": null, + "allow_failure": false, "created_at": "2016-01-11T10:13:33.506Z", "started_at": null, "finished_at": null, diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 1ba22070abe..67e1d316f02 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -58,8 +58,29 @@ the need to specify the value itself. There are two types of variables supported by GitLab: -- `env_var`: the runner will create environment variable named same as the variable key and set its value to the variable value. -- `file`: the runner will write the variable value to a temporary file and set the path to this file as the value of an environment variable named same as the variable key. +- "Variable": the Runner will create an environment variable named same as the variable key and set its value to the variable value. +- "File": the Runner will write the variable value to a temporary file and set the path to this file as the value of an environment variable named same as the variable key. + +Many tools (like [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) and [kubectl](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#the-kubeconfig-environment-variable)) provide the ability to customise configuration using files by either providing the file path as a command line argument or an environment variable. Prior to the introduction of variable types, the common pattern was to use the value of a CI variable, save it in a file, and then use the newly created file in your script: + +```bash +# Save the content of variable in a file +echo "$KUBE_CA_PEM" > "$(pwd)/kube.ca.pem" + # Use the newly created file +kubectl config set-cluster e2e --server="$KUBE_URL" --certificate-authority="$(pwd)/kube.ca.pem" +``` + +This can be simplified by creating a variable of type "File" and using it directly. For example, let's say we have the following variables. + +![CI/CD settings - variable types usage example](img/variable_types_usage_example.png) + +We can then call them from `.gitlab-ci.yml` like this: + +```bash +kubectl config set-cluster e2e --server="$KUBE_URL" --certificate-authority="$KUBE_CA_PEM" +``` + +Variable types can be set via the [UI](#via-the-ui) or the [API](../../api/project_level_variables.md#create-variable), but not in `.gitlab-ci.yml`. #### Masked variables diff --git a/doc/ci/variables/img/new_custom_variables_example.png b/doc/ci/variables/img/new_custom_variables_example.png Binary files differindex 4b78e0ff587..efe104efe4c 100644 --- a/doc/ci/variables/img/new_custom_variables_example.png +++ b/doc/ci/variables/img/new_custom_variables_example.png diff --git a/doc/ci/variables/img/variable_types_usage_example.png b/doc/ci/variables/img/variable_types_usage_example.png Binary files differnew file mode 100644 index 00000000000..0e8bde891fe --- /dev/null +++ b/doc/ci/variables/img/variable_types_usage_example.png diff --git a/doc/development/changelog.md b/doc/development/changelog.md index 273a7fceaf5..45b3d5a23a1 100644 --- a/doc/development/changelog.md +++ b/doc/development/changelog.md @@ -35,6 +35,7 @@ the `author` field. GitLab team members **should not**. - Any user-facing change **should** have a changelog entry. Example: "GitLab now uses system fonts for all text." +- Any change behind a feature flag **should not** have a changelog entry. The entry should be added [in the merge request removing the feature flags](https://docs.gitlab.com/ee/development/feature_flags.html#developing-with-feature-flags). - A fix for a regression introduced and then fixed in the same release (i.e., fixing a bug introduced during a monthly release candidate) **should not** have a changelog entry. diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md index faea8d5982f..eb492c9818b 100644 --- a/doc/development/i18n/proofreader.md +++ b/doc/development/i18n/proofreader.md @@ -56,7 +56,6 @@ are very appreciative of the work done by translators and proofreaders! - Italian - Paolo Falomo - [GitLab](https://gitlab.com/paolofalomo), [Crowdin](https://crowdin.com/profile/paolo.falomo) - Japanese - - Yamana Tokiuji - [GitLab](https://gitlab.com/tokiuji), [Crowdin](https://crowdin.com/profile/yamana) - Hiroyuki Sato - [GitLab](https://gitlab.com/hiroponz), [Crowdin](https://crowdin.com/profile/hiroponz) - Tomo Dote - [Gitlab](https://gitlab.com/fu7mu4), [Crowdin](https://crowdin.com/profile/fu7mu4) - Korean @@ -72,6 +71,7 @@ are very appreciative of the work done by translators and proofreaders! - Maksymilian Roman - [GitLab](https://gitlab.com/villaincandle), [Crowdin](https://crowdin.com/profile/villaincandle) - Portuguese - Proofreaders needed. + - Diogo Trindade - [GitLab](https://gitlab.com/luisdiogo2071317), [Crowdin](https://crowdin.com/profile/ldiogotrindade) - Portuguese, Brazilian - Paulo George Gomes Bezerra - [GitLab](https://gitlab.com/paulobezerra), [Crowdin](https://crowdin.com/profile/paulogomes.rep) - André Gama - [GitLab](https://gitlab.com/andregamma), [Crowdin](https://crowdin.com/profile/ToeOficial) diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index 9bd99e80357..58d6f08954d 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -15,10 +15,8 @@ information on general testing practices at GitLab. ## Jest -GitLab has started to migrate tests to the [Jest](https://jestjs.io) -testing framework. You can read a [detailed evaluation](https://gitlab.com/gitlab-org/gitlab-ce/issues/49171) -of Jest compared to our use of Karma and Jasmine. In summary, it will allow us -to improve the performance and consistency of our frontend tests. +We have started to migrate frontend tests to the [Jest](https://jestjs.io) testing framework (see also the corresponding +[epic](https://gitlab.com/groups/gitlab-org/-/epics/895)). Jest tests can be found in `/spec/frontend` and `/ee/spec/frontend` in EE. @@ -26,6 +24,17 @@ It is not yet a requirement to use Jest. You can view the [epic](https://gitlab.com/groups/gitlab-org/-/epics/873) of issues we need to solve before being able to use Jest for all our needs. +### Differences to Karma + +- Jest runs in a Node.js environment, not in a browser. Support for running Jest tests in a browser [is planned](https://gitlab.com/gitlab-org/gitlab-ce/issues/58205). +- Because Jest runs in a Node.js environment, it uses [jsdom](https://github.com/jsdom/jsdom) by default. +- All calls to `setTimeout` and `setInterval` are mocked away. See also [Jest Timer Mocks](https://jestjs.io/docs/en/timer-mocks). +- `rewire` is not required because Jest supports mocking modules. See also [Manual Mocks](https://jestjs.io/docs/en/manual-mocks). +- The following will cause tests to fail in Jest: + - Unmocked requests. + - Unhandled Promise rejections. + - Calls to `console.warn`, including warnings from libraries like Vue. + ### Debugging Jest tests Running `yarn jest-debug` will run Jest in debug mode, allowing you to debug/inspect as described in the [Jest docs](https://jestjs.io/docs/en/troubleshooting#tests-are-failing-and-you-don-t-know-why). diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md index d1d12dfd064..15176ede733 100644 --- a/doc/integration/elasticsearch.md +++ b/doc/integration/elasticsearch.md @@ -340,10 +340,30 @@ curl --request PUT localhost:9200/gitlab-production/_settings --data '{ } }' ``` -A force merge should be called after enabling the refreshing above: +A force merge should be called after enabling the refreshing above. + +For Elasticsearch 6.x, before proceeding with the force merge, the index should be in read-only mode: + +```bash +curl --request PUT localhost:9200/gitlab-production/_settings --data '{ + "settings": { + "index.blocks.write": true + } }' +``` + +Then, initiate the force merge: ```bash -curl --request POST 'http://localhost:9200/_forcemerge?max_num_segments=5' +curl --request POST 'http://localhost:9200/gitlab-production/_forcemerge?max_num_segments=5' +``` + +After this, if your index is in read-only, switch back to read-write: + +```bash +curl --request PUT localhost:9200/gitlab-production/_settings --data '{ + "settings": { + "index.blocks.write": false + } }' ``` Enable Elasticsearch search in **Admin > Settings > Integrations**. That's it. Enjoy it! diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index dd4e96c1f4a..d2995d48833 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -61,16 +61,32 @@ Click the **All**, **Private**, **Internal**, or **Public** tab to list only pro criteria. By default, all projects are listed, in reverse order of when they were last updated. For each -project, the name, namespace, description, and size is listed, also options to **Edit** or -**Delete** it. +project, the following information is listed: -Sort projects by **Name**, **Last created**, **Oldest created**, **Last updated**, **Oldest -updated**, **Owner**, and choose to hide or show archived projects. +- Name. +- Namespace. +- Description. +- Size, updated every 15 minutes at most. + +Projects can be edited or deleted. + +The list of projects can be sorted by: + +- Name. +- Last created. +- Oldest created. +- Last updated. +- Oldest updated. +- Owner. + +A user can choose to hide or show archived projects in the list. In the **Filter by name** field, type the project name you want to find, and GitLab will filter them as you type. Select from the **Namespace** dropdown to filter only projects in that namespace. -You can combine the filter options. For example, click the **Public** tab, and enter `score` in -the **Filter by name...** input box to list only public projects with `score` in their name. +You can combine the filter options. For example, to list only public projects with `score` in their name: + +1. Click the **Public** tab. +1. Enter `score` in the **Filter by name...** input box. diff --git a/doc/user/group/insights/img/insights_sidebar_link.png b/doc/user/group/insights/img/insights_sidebar_link.png Binary files differnew file mode 100644 index 00000000000..64bbd1fc4d3 --- /dev/null +++ b/doc/user/group/insights/img/insights_sidebar_link.png diff --git a/doc/user/group/insights/index.md b/doc/user/group/insights/index.md index 5283d8da77d..20f76c54ae7 100644 --- a/doc/user/group/insights/index.md +++ b/doc/user/group/insights/index.md @@ -4,8 +4,8 @@ CAUTION: **Beta:** Insights is considered beta, and is not ready for production use. -Follow [gitlab-org&725](https://gitlab.com/groups/gitlab-org/-/epics/725) for -updates. +Follow [gitlab-org/quality/team-tasks#137](https://gitlab.com/gitlab-org/quality/team-tasks/issues/137#general-availability) +for updates. Configure the Insights that matter for your groups to explore data such as triage hygiene, issues created/closed per a given period, average time for merge @@ -13,6 +13,13 @@ requests to be merged and much more. ![Insights example stacked bar chart](img/insights_example_stacked_bar_chart.png) +## View your group's Insights + +You can access your group's Insights by clicking the **Overview > Insights** +link in the left sidebar: + +![Insights sidebar link](img/insights_sidebar_link.png) + ## Configure your Insights Navigate to your group's **Settings > General**, expand **Insights**, and choose diff --git a/doc/user/project/insights/img/insights_sidebar_link.png b/doc/user/project/insights/img/insights_sidebar_link.png Binary files differnew file mode 100644 index 00000000000..aadb5745992 --- /dev/null +++ b/doc/user/project/insights/img/insights_sidebar_link.png diff --git a/doc/user/project/insights/index.md b/doc/user/project/insights/index.md index 720f4c6604e..b6cc1862cc2 100644 --- a/doc/user/project/insights/index.md +++ b/doc/user/project/insights/index.md @@ -4,18 +4,25 @@ CAUTION: **Beta:** Insights is considered beta, and is not ready for production use. -Follow [gitlab-org&725](https://gitlab.com/groups/gitlab-org/-/epics/725) for -updates. +Follow [gitlab-org/quality/team-tasks#137](https://gitlab.com/gitlab-org/quality/team-tasks/issues/137#general-availability) +for updates. Configure the Insights that matter for your projects to explore data such as triage hygiene, issues created/closed per a given period, average time for merge requests to be merged and much more. -![Insights example stacked bar chart](img/project_insights.png) +![Insights example bar chart](img/project_insights.png) NOTE: **Note:** This feature is [also available at the group level](https://docs.gitlab.com/ee/user/group/insights/index.html). +## View your project's Insights + +You can access your project's Insights by clicking the **Project > Insights** +link in the left sidebar: + +![Insights sidebar link](img/insights_sidebar_link.png) + ## Configure your Insights Insights are configured using a YAML file called `.gitlab/insights.yml` within @@ -74,8 +81,7 @@ Each chart definition is made up of a hash composed of key-value pairs. For example, here's single chart definition: ```yaml -monthlyBugsCreated: - title: Monthly Bugs Created (bar) +- title: Monthly Bugs Created (bar) type: bar query: issuable_type: issue diff --git a/doc/user/project/integrations/img/mattermost_configuration.png b/doc/user/project/integrations/img/mattermost_configuration.png Binary files differindex e0b55b23520..75ef0310f2d 100644 --- a/doc/user/project/integrations/img/mattermost_configuration.png +++ b/doc/user/project/integrations/img/mattermost_configuration.png diff --git a/doc/user/project/integrations/img/slack_configuration.png b/doc/user/project/integrations/img/slack_configuration.png Binary files differindex 53b30e0e8cd..a14d2969488 100644 --- a/doc/user/project/integrations/img/slack_configuration.png +++ b/doc/user/project/integrations/img/slack_configuration.png diff --git a/doc/user/project/integrations/mattermost.md b/doc/user/project/integrations/mattermost.md index 8c5461de42f..d7fd75fd728 100644 --- a/doc/user/project/integrations/mattermost.md +++ b/doc/user/project/integrations/mattermost.md @@ -27,9 +27,11 @@ There, you will see a checkbox with the following events that can be triggered: - Confidential issue - Merge request - Note +- Confidential note - Tag push - Pipeline - Wiki page +- Deployment Below each of these event checkboxes, you have an input field to enter which Mattermost channel you want to send that event message. Enter your preferred channel handle (the hash sign `#` is optional). diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index 40113624430..751e8e44e60 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -168,7 +168,7 @@ Alerts can be used to trigger actions, like open an issue automatically. To conf 1. Optionally, select whether to send an email notification to the developers of the project. 1. Click **Save changes**. -Once enabled, an issue will be opened automatically when an alert is triggered. To further customize the issue, you can add labels, mentions, or any other supported [quick action](../quick_actions.md) in the selected issue template. +Once enabled, an issue will be opened automatically when an alert is triggered. The author of the issue will be the GitLab Alert Bot. To further customize the issue, you can add labels, mentions, or any other supported [quick action](../quick_actions.md) in the selected issue template. If the metric exceeds the threshold of the alert for over 5 minutes, an email will be sent to all [Maintainers and Owners](../../permissions.md#project-members-permissions) of the project. diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index 723195224d0..09736c7fc7e 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -547,13 +547,13 @@ Add the following alias to your `~/.gitconfig`: Now you can check out a particular merge request from any repository and any remote. For example, to check out the merge request with ID 5 as shown in GitLab -from the `upstream` remote, do: +from the `origin` remote, do: ``` -git mr upstream 5 +git mr origin 5 ``` -This will fetch the merge request into a local `mr-upstream-5` branch and check +This will fetch the merge request into a local `mr-origin-5` branch and check it out. #### Checkout locally by modifying `.git/config` for a given repository diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md index 97ecc4c0d65..cb514b76a4e 100644 --- a/doc/user/project/repository/index.md +++ b/doc/user/project/repository/index.md @@ -173,11 +173,15 @@ Via command line, you can commit multiple times before pushing. ## Repository size -On GitLab.com, your [repository size limit is 10GB](../../gitlab_com/index.md#repository-size-limit) -(including LFS). For other instances, the repository size is limited by your -system administrators. +A project's repository size is reported on the project's **Details** page. The reported size is +updated every 15 minutes at most, so may not reflect recent activity. -You can also [reduce a repository size using Git](reducing_the_repo_size_using_git.md). +The repository size for: + +- GitLab.com [is set by GitLab](../../gitlab_com/index.md#repository-size-limit). +- Self-managed instances is set by your GitLab administrators. + +You can [reduce a repository's size using Git](reducing_the_repo_size_using_git.md). ## Contributors diff --git a/lib/haml_lint/inline_javascript.rb b/haml_lint/inline_javascript.rb index 1b17162f71d..da6af92e82b 100644 --- a/lib/haml_lint/inline_javascript.rb +++ b/haml_lint/inline_javascript.rb @@ -7,7 +7,7 @@ unless Rails.env.production? module HamlLint class Linter::InlineJavaScript < Linter - include LinterRegistry + include ::HamlLint::LinterRegistry def visit_filter(node) return unless node.filter_type == 'javascript' diff --git a/haml_lint/linter/no_plain_nodes.rb b/haml_lint/linter/no_plain_nodes.rb new file mode 100644 index 00000000000..c39f61fa80d --- /dev/null +++ b/haml_lint/linter/no_plain_nodes.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require 'active_support/core_ext/array/grouping' + +module HamlLint + class Linter + class NoPlainNodes < Linter + include ::HamlLint::LinterRegistry + + def visit_tag(node) + if inline_plain_node?(node) + check_inline(node) + elsif !node.script.empty? + check_script(node) + else + check(node) + end + end + + private + + def check(node) + text_in_node(node).each { |string| record(node, string) } + end + + def check_inline(node) + text = inline_text(node) + record(node, text) unless text.empty? + end + + def check_script(node) + text = inline_text(node) + record(node, text) unless text.start_with?('=') || text.empty? + end + + # Build an array of all strings in child text nodes. + # non text nodes are nil, where we'll split the sentences. + def text_in_node(node) + texts = node.children.map do |child| + child.text.strip if text_node?(child) + end + + texts.split(nil).map { |sentence| sentence.join(' ') unless sentence.empty? }.compact + end + + # Removes a node's attributes and tag from the source code, + # returning the inline text of a node. + def inline_text(node) + text = node.source_code.gsub("%#{node.tag_name}", '') + + attributes = node.attributes_source.map(&:last) + attributes.each { |attribute| text = text.gsub(attribute, '') } + + text.strip + end + + def record(node, string) + record_lint(node, message(string)) + end + + def message(string) + "`#{string}` is a plain node. Please use an i18n method like `#{fixed(string)}`" + end + + def fixed(string) + "= _('#{string}')" + end + + def inline_plain_node?(node) + node.children.empty? && node.script.empty? + end + + def plain_node?(node) + node.is_a?(::HamlLint::Tree::PlainNode) + end + + def text_node?(node) + return false unless plain_node?(node) + + !node.text.empty? + end + end + end +end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 625fada4f08..1a3318fe849 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -1266,7 +1266,7 @@ module API end class JobBasic < Grape::Entity - expose :id, :status, :stage, :name, :ref, :tag, :coverage + expose :id, :status, :stage, :name, :ref, :tag, :coverage, :allow_failure expose :created_at, :started_at, :finished_at expose :duration expose :user, with: User diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index daa98c22e5e..ce85772e4ed 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -367,10 +367,6 @@ module API merge_request = find_project_merge_request(params[:merge_request_iid]) merge_when_pipeline_succeeds = to_boolean(params[:merge_when_pipeline_succeeds]) - if merge_when_pipeline_succeeds || merge_request.merge_when_pipeline_succeeds - render_api_error!('Not allowed: pipeline does not exist', 405) unless merge_request.head_pipeline - end - # Merge request can not be merged # because user dont have permissions to push into target branch unauthorized! unless merge_request.can_be_merged_by?(current_user) diff --git a/lib/gitlab/ci/templates/Docker.gitlab-ci.yml b/lib/gitlab/ci/templates/Docker.gitlab-ci.yml index eeefadaa019..f6d240b7b6d 100644 --- a/lib/gitlab/ci/templates/Docker.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Docker.gitlab-ci.yml @@ -1,14 +1,11 @@ -# Official docker image. -image: docker:latest - -services: - - docker:dind - -before_script: - - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - build-master: + # Official docker image. + image: docker:latest stage: build + services: + - docker:dind + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - docker build --pull -t "$CI_REGISTRY_IMAGE" . - docker push "$CI_REGISTRY_IMAGE" @@ -16,7 +13,13 @@ build-master: - master build: + # Official docker image. + image: docker:latest stage: build + services: + - docker:dind + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" . - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" diff --git a/lib/tasks/haml-lint.rake b/lib/tasks/haml-lint.rake index 786efd14b1a..305e15d69d5 100644 --- a/lib/tasks/haml-lint.rake +++ b/lib/tasks/haml-lint.rake @@ -1,6 +1,6 @@ unless Rails.env.production? require 'haml_lint/rake_task' - require 'haml_lint/inline_javascript' + require Rails.root.join('haml_lint/inline_javascript') # Workaround for warnings from parser/current # Keep it even if it no longer emits any warnings, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index c20cae9665a..64d70f235b7 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -976,6 +976,9 @@ msgstr "" msgid "Any" msgstr "" +msgid "Any Label" +msgstr "" + msgid "Any Milestone" msgstr "" @@ -1186,6 +1189,14 @@ msgstr "" msgid "Attach a file by drag & drop or %{upload_link}" msgstr "" +msgid "Attaching a file" +msgid_plural "Attaching %d files" +msgstr[0] "" +msgstr[1] "" + +msgid "Attaching the file failed." +msgstr "" + msgid "Aug" msgstr "" @@ -4205,6 +4216,12 @@ msgstr "" msgid "Failed to connect to the prometheus server" msgstr "" +msgid "Failed to create Merge Request. Please try again." +msgstr "" + +msgid "Failed to create a branch for this issue. Please try again." +msgstr "" + msgid "Failed to create repository via gitlab-shell" msgstr "" @@ -4214,6 +4231,9 @@ msgstr "" msgid "Failed to deploy to" msgstr "" +msgid "Failed to get ref." +msgstr "" + msgid "Failed to install." msgstr "" @@ -4327,6 +4347,9 @@ msgstr "" msgid "Files" msgstr "" +msgid "Files, directories, and submodules in the path %{path} for commit reference %{ref}" +msgstr "" + msgid "Filter" msgstr "" @@ -5523,6 +5546,9 @@ msgstr "" msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed." msgstr "" +msgid "Labels|and %{count} more" +msgstr "" + msgid "Language" msgstr "" @@ -6037,7 +6063,10 @@ msgstr "" msgid "Milestones|Promote Milestone" msgstr "" -msgid "Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}. Existing project milestones with the same title will be merged. This action cannot be reversed." +msgid "Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}. Existing project milestones with the same title will be merged." +msgstr "" + +msgid "Milestones|This action cannot be reversed." msgstr "" msgid "Mirror a repository" @@ -6291,6 +6320,9 @@ msgstr "" msgid "No %{providerTitle} repositories available to import" msgstr "" +msgid "No Label" +msgstr "" + msgid "No Milestone" msgstr "" @@ -7541,6 +7573,12 @@ msgstr "" msgid "ProjectPage|Project ID: %{project_id}" msgstr "" +msgid "ProjectSelect| or group" +msgstr "" + +msgid "ProjectSelect|Search for project" +msgstr "" + msgid "ProjectSettings|Additional merge request capabilities that influence how and when merges will be performed" msgstr "" @@ -8232,6 +8270,12 @@ msgstr "" msgid "Revoked personal access token %{personal_access_token_name}!" msgstr "" +msgid "RightSidebar|adding a" +msgstr "" + +msgid "RightSidebar|deleting the" +msgstr "" + msgid "Run housekeeping" msgstr "" @@ -9673,6 +9717,9 @@ msgstr "" msgid "There was a problem communicating with your device." msgstr "" +msgid "There was an error %{message} todo." +msgstr "" + msgid "There was an error loading users activity calendar." msgstr "" @@ -10361,6 +10408,9 @@ msgstr "" msgid "Unable to schedule a pipeline to run immediately" msgstr "" +msgid "Unable to update label prioritization at this time" +msgstr "" + msgid "Unable to update this issue at this time." msgstr "" diff --git a/qa/qa/resource/ci_variable.rb b/qa/qa/resource/ci_variable.rb index d82de4cb816..341d3c1ed7e 100644 --- a/qa/qa/resource/ci_variable.rb +++ b/qa/qa/resource/ci_variable.rb @@ -25,6 +25,33 @@ module QA end end end + + def fabricate_via_api! + resource_web_url(api_get) + rescue ResourceNotFoundError + super + end + + def resource_web_url(resource) + super + rescue ResourceURLMissingError + # this particular resource does not expose a web_url property + end + + def api_get_path + "/projects/#{project.id}/variables/#{key}" + end + + def api_post_path + "/projects/#{project.id}/variables" + end + + def api_post_body + { + key: key, + value: value + } + end end end end diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index de1e9f04c36..c1a0cff86d8 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -7,6 +7,7 @@ module QA class Project < Base include Events::Project + attribute :id attribute :name attribute :description diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index a5218fc9ab1..174a52bd376 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -76,6 +76,9 @@ module QA # https://developers.google.com/web/updates/2017/04/headless-chrome#cli options.add_argument("disable-gpu") end + + # Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab-ee/issues/4252 + options.add_argument("disable-dev-shm-usage") if QA::Runtime::Env.running_in_ci? end # Use the same profile on QA runs if CHROME_REUSE_PROFILE is true. @@ -85,9 +88,6 @@ module QA options.add_argument("user-data-dir=#{qa_profile_dir}") end - # Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab-ee/issues/4252 - options.add_argument("disable-dev-shm-usage") if QA::Runtime::Env.running_in_ci? - selenium_options = { browser: QA::Runtime::Env.browser, clear_local_storage: true, diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb index 358ab04eadc..9b083d59a5e 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb @@ -13,7 +13,8 @@ module QA expect(page).to have_content(issue_title) end - context 'when using attachments in comments', :object_storage do + # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/101 + context 'when using attachments in comments', :object_storage, :quarantine do let(:file_to_attach) do File.absolute_path(File.join('spec', 'fixtures', 'banana_sample.gif')) end diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb index 33f342edb08..561a8895329 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb @@ -3,15 +3,25 @@ module QA context 'Verify' do describe 'CI variable support' do - it 'user adds a CI variable' do + it 'user adds a CI variable', :smoke do Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } + Page::Main::Login.perform(&:sign_in_using_credentials) + + project = Resource::Project.fabricate! do |project| + project.name = 'project-with-ci-variables' + project.description = 'project with CI variables' + end Resource::CiVariable.fabricate! do |resource| + resource.project = project resource.key = 'VARIABLE_KEY' resource.value = 'some_CI_variable' end + project.visit! + + Page::Project::Menu.perform(&:go_to_ci_cd_settings) + Page::Project::Settings::CICD.perform do |settings| settings.expand_ci_variables do |page| expect(page).to have_field(with: 'VARIABLE_KEY') diff --git a/spec/fixtures/api/schemas/public_api/v4/job.json b/spec/fixtures/api/schemas/public_api/v4/job.json index 454935422a0..c038ae0a664 100644 --- a/spec/fixtures/api/schemas/public_api/v4/job.json +++ b/spec/fixtures/api/schemas/public_api/v4/job.json @@ -28,6 +28,7 @@ "ref": { "type": "string" }, "tag": { "type": "boolean" }, "coverage": { "type": ["number", "null"] }, + "allow_failure": { "type": "boolean" }, "created_at": { "type": "string" }, "started_at": { "type": ["null", "string"] }, "finished_at": { "type": ["null", "string"] }, diff --git a/spec/frontend/repository/components/table/index_spec.js b/spec/frontend/repository/components/table/index_spec.js new file mode 100644 index 00000000000..6f52cffe077 --- /dev/null +++ b/spec/frontend/repository/components/table/index_spec.js @@ -0,0 +1,47 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlLoadingIcon } from '@gitlab/ui'; +import Table from '~/repository/components/table/index.vue'; + +let vm; + +function factory(path, loading = false) { + vm = shallowMount(Table, { + propsData: { + path, + }, + mocks: { + $apollo: { + queries: { + files: { loading }, + }, + }, + }, + }); +} + +describe('Repository table component', () => { + afterEach(() => { + vm.destroy(); + }); + + it.each` + path | ref + ${'/'} | ${'master'} + ${'app/assets'} | ${'master'} + ${'/'} | ${'test'} + `('renders table caption for $ref in $path', ({ path, ref }) => { + factory(path); + + vm.setData({ ref }); + + expect(vm.find('caption').text()).toEqual( + `Files, directories, and submodules in the path ${path} for commit reference ${ref}`, + ); + }); + + it('renders loading icon', () => { + factory('/', true); + + expect(vm.find(GlLoadingIcon).exists()).toBe(true); + }); +}); diff --git a/spec/graphql/resolvers/group_resolver_spec.rb b/spec/graphql/resolvers/group_resolver_spec.rb new file mode 100644 index 00000000000..5eb9cd06d15 --- /dev/null +++ b/spec/graphql/resolvers/group_resolver_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::GroupResolver do + include GraphqlHelpers + + set(:group1) { create(:group) } + set(:group2) { create(:group) } + + describe '#resolve' do + it 'batch-resolves groups by full path' do + paths = [group1.full_path, group2.full_path] + + result = batch(max_queries: 1) do + paths.map { |path| resolve_group(path) } + end + + expect(result).to contain_exactly(group1, group2) + end + + it 'resolves an unknown full_path to nil' do + result = batch { resolve_group('unknown/project') } + + expect(result).to be_nil + end + end + + def resolve_group(full_path) + resolve(described_class, args: { full_path: full_path }) + end +end diff --git a/spec/haml_lint/linter/no_plain_nodes_spec.rb b/spec/haml_lint/linter/no_plain_nodes_spec.rb new file mode 100644 index 00000000000..08deb5a4e9e --- /dev/null +++ b/spec/haml_lint/linter/no_plain_nodes_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'haml_lint' +require 'haml_lint/spec' +require Rails.root.join('haml_lint/linter/no_plain_nodes') + +describe HamlLint::Linter::NoPlainNodes do + include_context 'linter' + + context 'reports when a tag has an inline plain node' do + let(:haml) { '%tag Hello Tanuki' } + let(:message) { "`Hello Tanuki` is a plain node. Please use an i18n method like `= _('Hello Tanuki')`" } + + it { is_expected.to report_lint message: message } + end + + context 'reports when a tag has multiline plain nodes' do + let(:haml) { <<-HAML } + %tag + Hello + Tanuki + HAML + + it { is_expected.to report_lint count: 1 } + end + + context 'reports when a tag has an inline plain node with interpolation' do + let(:haml) { '%tag Hello #{"Tanuki"}!' } # rubocop:disable Lint/InterpolationCheck + + it { is_expected.to report_lint } + end + + context 'does not report when a tag has an inline script' do + let(:haml) { '%tag= "Hello Tanuki"' } + + it { is_expected.not_to report_lint } + end + + context 'does not report when a tag is empty' do + let(:haml) { '%tag' } + + it { is_expected.not_to report_lint } + end + + context 'reports multiple when a tag has multiline plain nodes split by non-text nodes' do + let(:haml) { <<-HAML } + %tag + Hello + .split-node There + Tanuki + HAML + + it { is_expected.to report_lint count: 3 } + end +end diff --git a/spec/helpers/groups/group_members_helper_spec.rb b/spec/helpers/groups/group_members_helper_spec.rb new file mode 100644 index 00000000000..898c330c498 --- /dev/null +++ b/spec/helpers/groups/group_members_helper_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Groups::GroupMembersHelper do + describe '.group_member_select_options' do + let(:group) { create(:group) } + + before do + helper.instance_variable_set(:@group, group) + end + + it 'returns an options hash' do + expect(helper.group_member_select_options).to include(multiple: true, scope: :all, email_user: true) + end + end +end diff --git a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js b/spec/javascripts/dirty_submit/dirty_submit_form_spec.js index 95cc90dcb0f..b1017e0c4f0 100644 --- a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js +++ b/spec/javascripts/dirty_submit/dirty_submit_form_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import DirtySubmitForm from '~/dirty_submit/dirty_submit_form'; import { getInputValue, setInputValue, createForm } from './helper'; @@ -13,46 +14,100 @@ function expectToToggleDisableOnDirtyUpdate(submit, input) { } describe('DirtySubmitForm', () => { - DirtySubmitForm.THROTTLE_DURATION = 0; + const originalThrottleDuration = DirtySubmitForm.THROTTLE_DURATION; - it('disables submit until there are changes', done => { - const { form, input, submit } = createForm(); + describe('submit button tests', () => { + beforeEach(() => { + DirtySubmitForm.THROTTLE_DURATION = 0; + }); - new DirtySubmitForm(form); // eslint-disable-line no-new + afterEach(() => { + DirtySubmitForm.THROTTLE_DURATION = originalThrottleDuration; + }); - return expectToToggleDisableOnDirtyUpdate(submit, input) - .then(done) - .catch(done.fail); - }); + it('disables submit until there are changes', done => { + const { form, input, submit } = createForm(); - it('disables submit until there are changes when initializing with a falsy value', done => { - const { form, input, submit } = createForm(); - input.value = ''; + new DirtySubmitForm(form); // eslint-disable-line no-new - new DirtySubmitForm(form); // eslint-disable-line no-new + return expectToToggleDisableOnDirtyUpdate(submit, input) + .then(done) + .catch(done.fail); + }); - return expectToToggleDisableOnDirtyUpdate(submit, input) - .then(done) - .catch(done.fail); - }); + it('disables submit until there are changes when initializing with a falsy value', done => { + const { form, input, submit } = createForm(); + input.value = ''; + + new DirtySubmitForm(form); // eslint-disable-line no-new + + return expectToToggleDisableOnDirtyUpdate(submit, input) + .then(done) + .catch(done.fail); + }); - it('disables submit until there are changes for radio inputs', done => { - const { form, input, submit } = createForm('radio'); + it('disables submit until there are changes for radio inputs', done => { + const { form, input, submit } = createForm('radio'); - new DirtySubmitForm(form); // eslint-disable-line no-new + new DirtySubmitForm(form); // eslint-disable-line no-new - return expectToToggleDisableOnDirtyUpdate(submit, input) - .then(done) - .catch(done.fail); + return expectToToggleDisableOnDirtyUpdate(submit, input) + .then(done) + .catch(done.fail); + }); + + it('disables submit until there are changes for checkbox inputs', done => { + const { form, input, submit } = createForm('checkbox'); + + new DirtySubmitForm(form); // eslint-disable-line no-new + + return expectToToggleDisableOnDirtyUpdate(submit, input) + .then(done) + .catch(done.fail); + }); }); - it('disables submit until there are changes for checkbox inputs', done => { - const { form, input, submit } = createForm('checkbox'); + describe('throttling tests', () => { + beforeEach(() => { + jasmine.clock().install(); + DirtySubmitForm.THROTTLE_DURATION = 100; + }); + + afterEach(() => { + jasmine.clock().uninstall(); + DirtySubmitForm.THROTTLE_DURATION = originalThrottleDuration; + }); + + it('throttles updates when rapid changes are made to a single form element', () => { + const { form, input } = createForm(); + const updateDirtyInputSpy = spyOn(new DirtySubmitForm(form), 'updateDirtyInput'); + + _.range(10).forEach(i => { + setInputValue(input, `change ${i}`, false); + }); + + jasmine.clock().tick(101); + + expect(updateDirtyInputSpy).toHaveBeenCalledTimes(2); + }); + + it('does not throttle updates when rapid changes are made to different form elements', () => { + const form = document.createElement('form'); + const range = _.range(10); + range.forEach(i => { + form.innerHTML += `<input type="text" name="input-${i}" class="js-input-${i}"/>`; + }); + + const updateDirtyInputSpy = spyOn(new DirtySubmitForm(form), 'updateDirtyInput'); + + range.forEach(i => { + const input = form.querySelector(`.js-input-${i}`); + setInputValue(input, `change`, false); + }); - new DirtySubmitForm(form); // eslint-disable-line no-new + jasmine.clock().tick(101); - return expectToToggleDisableOnDirtyUpdate(submit, input) - .then(done) - .catch(done.fail); + expect(updateDirtyInputSpy).toHaveBeenCalledTimes(range.length); + }); }); }); diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb index 451dc88880c..0e1aed42cc5 100644 --- a/spec/presenters/merge_request_presenter_spec.rb +++ b/spec/presenters/merge_request_presenter_spec.rb @@ -403,7 +403,7 @@ describe MergeRequestPresenter do allow(resource).to receive(:source_branch_exists?) { true } is_expected - .to eq("/#{resource.source_project.full_path}/branches/#{resource.source_branch}") + .to eq("/#{resource.source_project.full_path}/-/branches/#{resource.source_branch}") end end @@ -426,7 +426,7 @@ describe MergeRequestPresenter do allow(resource).to receive(:target_branch_exists?) { true } is_expected - .to eq("/#{resource.source_project.full_path}/branches/#{resource.target_branch}") + .to eq("/#{resource.source_project.full_path}/-/branches/#{resource.target_branch}") end end diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index c14507de186..43462913497 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -286,6 +286,7 @@ describe API::Jobs do expect(json_response['ref']).to eq(job.ref) expect(json_response['tag']).to eq(job.tag) expect(json_response['coverage']).to eq(job.coverage) + expect(json_response['allow_failure']).to eq(job.allow_failure) expect(Time.parse(json_response['created_at'])).to be_like_time(job.created_at) expect(Time.parse(json_response['started_at'])).to be_like_time(job.started_at) expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at) diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 007f3517e64..5c94a87529b 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -1495,33 +1495,6 @@ describe API::MergeRequests do expect(json_response['merge_when_pipeline_succeeds']).to eq(true) end - context 'when the MR requires pipeline success' do - it 'returns 405 if the pipeline is missing' do - allow_any_instance_of(MergeRequest) - .to receive(:merge_when_pipeline_succeeds).and_return(true) - allow_any_instance_of(MergeRequest).to receive(:head_pipeline).and_return(nil) - - put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user) - - expect(response).to have_gitlab_http_status(405) - expect(json_response['message']).to eq('Not allowed: pipeline does not exist') - end - end - - context 'when the request requires pipeline success' do - it 'returns 405 if the pipeline is missing' do - allow_any_instance_of(MergeRequest) - .to receive(:merge_when_pipeline_succeeds).and_return(true) - allow_any_instance_of(MergeRequest).to receive(:head_pipeline).and_return(nil) - - put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user), - params: { merge_when_pipeline_succeeds: true } - - expect(response).to have_gitlab_http_status(405) - expect(json_response['message']).to eq('Not allowed: pipeline does not exist') - end - end - it "returns 404 for an invalid merge request IID" do put api("/projects/#{project.id}/merge_requests/12345/merge", user) diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index a0d01fc8263..dd9540c11d1 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -204,25 +204,27 @@ describe 'project routing' do describe Projects::BranchesController, 'routing' do it 'to #branches' do - expect(get('/gitlab/gitlabhq/branches')).to route_to('projects/branches#index', namespace_id: 'gitlab', project_id: 'gitlabhq') - expect(delete('/gitlab/gitlabhq/branches/feature%2345')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45') - expect(delete('/gitlab/gitlabhq/branches/feature%2B45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45') - expect(delete('/gitlab/gitlabhq/branches/feature@45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45') - expect(delete('/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz') - expect(delete('/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz') - expect(delete('/gitlab/gitlabhq/branches/feature@45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz') + expect(get('/gitlab/gitlabhq/-/branches')).to route_to('projects/branches#index', namespace_id: 'gitlab', project_id: 'gitlabhq') + expect(delete('/gitlab/gitlabhq/-/branches/feature%2345')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45') + expect(delete('/gitlab/gitlabhq/-/branches/feature%2B45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45') + expect(delete('/gitlab/gitlabhq/-/branches/feature@45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45') + expect(delete('/gitlab/gitlabhq/-/branches/feature%2345/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz') + expect(delete('/gitlab/gitlabhq/-/branches/feature%2B45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz') + expect(delete('/gitlab/gitlabhq/-/branches/feature@45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz') end + + it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/branches", "/gitlab/gitlabhq/-/branches" end describe Projects::TagsController, 'routing' do it 'to #tags' do - expect(get('/gitlab/gitlabhq/tags')).to route_to('projects/tags#index', namespace_id: 'gitlab', project_id: 'gitlabhq') - expect(delete('/gitlab/gitlabhq/tags/feature%2345')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45') - expect(delete('/gitlab/gitlabhq/tags/feature%2B45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45') - expect(delete('/gitlab/gitlabhq/tags/feature@45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45') - expect(delete('/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz') - expect(delete('/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz') - expect(delete('/gitlab/gitlabhq/tags/feature@45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz') + expect(get('/gitlab/gitlabhq/-/tags')).to route_to('projects/tags#index', namespace_id: 'gitlab', project_id: 'gitlabhq') + expect(delete('/gitlab/gitlabhq/-/tags/feature%2345')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45') + expect(delete('/gitlab/gitlabhq/-/tags/feature%2B45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45') + expect(delete('/gitlab/gitlabhq/-/tags/feature@45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45') + expect(delete('/gitlab/gitlabhq/-/tags/feature%2345/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz') + expect(delete('/gitlab/gitlabhq/-/tags/feature%2B45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz') + expect(delete('/gitlab/gitlabhq/-/tags/feature@45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz') end end @@ -247,6 +249,7 @@ describe 'project routing' do it_behaves_like 'RESTful project resources' do let(:actions) { [:index, :create, :destroy] } let(:controller) { 'protected_branches' } + let(:controller_path) { '/-/protected_branches' } end end @@ -592,18 +595,22 @@ describe 'project routing' do describe Projects::NetworkController, 'routing' do it 'to #show' do - expect(get('/gitlab/gitlabhq/network/master')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master') - expect(get('/gitlab/gitlabhq/network/ends-with.json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'ends-with.json') - expect(get('/gitlab/gitlabhq/network/master?format=json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json') + expect(get('/gitlab/gitlabhq/-/network/master')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master') + expect(get('/gitlab/gitlabhq/-/network/ends-with.json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'ends-with.json') + expect(get('/gitlab/gitlabhq/-/network/master?format=json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json') end + + it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/network/master", "/gitlab/gitlabhq/-/network/master" end describe Projects::GraphsController, 'routing' do it 'to #show' do - expect(get('/gitlab/gitlabhq/graphs/master')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master') - expect(get('/gitlab/gitlabhq/graphs/ends-with.json')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'ends-with.json') - expect(get('/gitlab/gitlabhq/graphs/master?format=json')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json') + expect(get('/gitlab/gitlabhq/-/graphs/master')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master') + expect(get('/gitlab/gitlabhq/-/graphs/ends-with.json')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'ends-with.json') + expect(get('/gitlab/gitlabhq/-/graphs/master?format=json')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json') end + + it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/graphs/master", "/gitlab/gitlabhq/-/graphs/master" end describe Projects::ForksController, 'routing' do @@ -661,4 +668,12 @@ describe 'project routing' do end end end + + describe Projects::Settings::RepositoryController, 'routing' do + it 'to #show' do + expect(get('/gitlab/gitlabhq/-/settings/repository')).to route_to('projects/settings/repository#show', namespace_id: 'gitlab', project_id: 'gitlabhq') + end + + it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/settings/repository", "/gitlab/gitlabhq/-/settings/repository" + end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 51c5a803dbd..4d33c6f4094 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -132,7 +132,7 @@ describe SystemNoteService do end it 'sets the note text' do - link = "http://localhost/#{project.full_path}/tags/#{tag_name}" + link = "http://localhost/#{project.full_path}/-/tags/#{tag_name}" expect(subject.note).to eq "tagged commit #{noteable.sha} to [`#{tag_name}`](#{link})" end diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb index 3e507fb133e..f6c613ad5aa 100644 --- a/spec/support/helpers/stub_configuration.rb +++ b/spec/support/helpers/stub_configuration.rb @@ -2,7 +2,8 @@ require 'active_support/core_ext/hash/transform_values' require 'active_support/hash_with_indifferent_access' require 'active_support/dependencies' -require_dependency 'gitlab' +# check gets rid of already initialized constant warnings when using spring +require_dependency 'gitlab' unless defined?(Gitlab) module StubConfiguration def stub_application_setting(messages) diff --git a/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb b/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb index f300bdd48b1..f326e502092 100644 --- a/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb +++ b/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb @@ -11,3 +11,11 @@ shared_examples 'redirecting a legacy path' do |source, target| expect(get(source)).not_to redirect_to(target) end end + +shared_examples 'redirecting a legacy project path' do |source, target| + include RSpec::Rails::RequestExampleGroup + + it "redirects #{source} to #{target}" do + expect(get(source)).to redirect_to(target) + end +end diff --git a/spec/views/help/index.html.haml_spec.rb b/spec/views/help/index.html.haml_spec.rb index 34e93d929a7..257991549a9 100644 --- a/spec/views/help/index.html.haml_spec.rb +++ b/spec/views/help/index.html.haml_spec.rb @@ -31,7 +31,7 @@ describe 'help/index' do render expect(rendered).to match '8.0.2' - expect(rendered).to have_link('8.0.2', href: %r{https://gitlab.com/gitlab-org/gitlab-(ce|ee)/tags/v8.0.2}) + expect(rendered).to have_link('8.0.2', href: %r{https://gitlab.com/gitlab-org/gitlab-(ce|ee)/-/tags/v8.0.2}) end it 'shows a link to the commit for pre-releases' do diff --git a/spec/workers/build_finished_worker_spec.rb b/spec/workers/build_finished_worker_spec.rb index 33f327d4a0c..4adb795b1d6 100644 --- a/spec/workers/build_finished_worker_spec.rb +++ b/spec/workers/build_finished_worker_spec.rb @@ -17,6 +17,7 @@ describe BuildFinishedWorker do expect_any_instance_of(BuildCoverageWorker).to receive(:perform) expect(BuildHooksWorker).to receive(:perform_async) expect(ArchiveTraceWorker).to receive(:perform_async) + expect(ExpirePipelineCacheWorker).to receive(:perform_async) described_class.new.perform(build.id) end |