diff options
113 files changed, 2597 insertions, 1572 deletions
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 529a0de696b..6551f47d3be 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -139,7 +139,7 @@ setup-test-env: rspec unit pg: <<: *rspec-metadata-pg - parallel: 20 + parallel: 25 rspec integration pg: <<: *rspec-metadata-pg @@ -152,7 +152,7 @@ rspec system pg: rspec unit pg-10: <<: *rspec-metadata-pg-10 <<: *only-schedules-master - parallel: 20 + parallel: 25 rspec integration pg-10: <<: *rspec-metadata-pg-10 diff --git a/.gitlab/issue_templates/Database Reviewer.md b/.gitlab/issue_templates/Database Reviewer.md deleted file mode 100644 index acbaf5c1965..00000000000 --- a/.gitlab/issue_templates/Database Reviewer.md +++ /dev/null @@ -1,34 +0,0 @@ -#### Database Reviewer Checklist - -Thank you for becoming a ~database reviewer! Please work on the list -below to complete your setup. For any question, reach out to #database -an mention `@abrandl`. - -- [ ] Change issue title to include your name: `Database Reviewer Checklist: Your Name` -- [ ] Review general [code review guide](https://docs.gitlab.com/ee/development/code_review.html) -- [ ] Review [database review documentation](https://about.gitlab.com/handbook/engineering/workflow/code-review/database.html) -- [ ] Familiarize with [migration helpers](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/database/migration_helpers.rb) and review usage in existing migrations -- [ ] Read [database migration style guide](https://docs.gitlab.com/ee/development/migration_style_guide.html) -- [ ] Familiarize with best practices in [database guides](https://docs.gitlab.com/ee/development/#database-guides) -- [ ] Watch [Optimising Rails Database Queries: Episode 1](https://www.youtube.com/watch?v=79GurlaxhsI) -- [ ] Read [Understanding EXPLAIN plans](https://docs.gitlab.com/ee/development/understanding_explain_plans.html) -- [ ] Review [database best practices](https://docs.gitlab.com/ee/development/#best-practices) -- [ ] Review how we use [database instances restored from a backup](https://ops.gitlab.net/gitlab-com/gl-infra/gitlab-restore/postgres-gprd) for testing and make sure you're set up to execute pipelines (check [README.md](https://ops.gitlab.net/gitlab-com/gl-infra/gitlab-restore/postgres-gprd/blob/master/README.md) and reach out to @abrandl since this is currently subject to being changed) -- [ ] Get yourself added to [`@gl-database`](https://gitlab.com/groups/gl-database/-/group_members) group and respond to @-mentions to the group (reach out to any maintainer on the group to get added). You will get TODOs on gitlab.com for group mentions. -- [ ] Make sure you have proper access to at least a read-only replica in staging and production -- [ ] Indicate in `data/team.yml` your role as a database reviewer ([example MR](https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/19600/diffs)). Assign MR to your manager for merge. -- [ ] Send one MR to improve the [review documentation](https://about.gitlab.com/handbook/engineering/workflow/code-review/database.html) or the [issue template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/issue_templates/Database%20Reviewer.md) - -Note that *approving and accepting* merge requests is *restricted* to -Database Maintainers only. As a reviewer, pass the MR to a maintainer -for approval. - -You're all set! Watch out for TODOs on GitLab.com. - -###### Where to go for questions? - -Reach out to `#database` on Slack and mention `@abrandl` for any questions. - -cc @abrandl - -/label ~meta ~database diff --git a/Dangerfile b/Dangerfile index d0a605f8d8e..094d55e8652 100644 --- a/Dangerfile +++ b/Dangerfile @@ -19,4 +19,5 @@ unless helper.release_automation? danger.import_dangerfile(path: 'danger/single_codebase') danger.import_dangerfile(path: 'danger/gitlab_ui_wg') danger.import_dangerfile(path: 'danger/ce_ee_vue_templates') + danger.import_dangerfile(path: 'danger/only_documentation') end diff --git a/PROCESS.md b/PROCESS.md index 07b150ea463..22b68b0aaca 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -84,43 +84,28 @@ star, smile, etc.). Some good tips about code reviews can be found in our [Code Review Guidelines]: https://docs.gitlab.com/ce/development/code_review.html -## Feature freeze on the 7th for the release on the 22nd - -The feature freeze on the 7th has been discontinued. The [transition period overview](https://gitlab.com/gitlab-org/release/docs/blob/21cbd409dd5f157fe252f254f3e897f01908abe2/general/deploy/auto-deploy-transition.md#transition) -describes the change to this process. During the transition period, the only guarantee that -a change will be included in the release on the 22nd is if the change has been -deployed to GitLab.com prior to this date. +## Feature flags -### Feature flags +Overview and details of feature flag processes in development of GitLab itself is described in [feature flags process documentation](https://docs.gitlab.com/ee/development/feature_flags/process.html). -Merge requests that make changes hidden behind a feature flag, or remove an -existing feature flag because a feature is deemed stable, may be merged (and -picked into the stable branches) up to the 19th of the month. Such merge -requests should have the ~"feature flag" label assigned, and don't require a -corresponding exception request to be created. +Guides on how to include feature flags in your backend/frontend code while developing GitLab are described in [developing with feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags/developing.html). -A level of common sense should be applied when deciding whether to have a feature -behind a feature flag off or on by default. +Getting access and how to expose the feature to users is detailed in [controlling feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags/controls.html). -The following guidelines can be applied to help make this decision: +## Feature proposals from the 22nd to the 1st -* If the feature is not fully ready or functioning, the feature flag should be disabled by default. -* If the feature is ready but there are concerns about performance or impact, the feature flag should be enabled by default, but -disabled via chatops before deployment on GitLab.com environments. If the performance concern is confirmed, the final release should have the feature flag disabled by default. -* In most other cases, the feature flag can be enabled by default. +To allow the Product and Engineering teams time to discuss issues that will be placed into an upcoming milestone, +Product Managers must have their proposal for that milestone ready by the 22nd of each month. -For more information on rolling out changes using feature flags, read [through the documentation](https://docs.gitlab.com/ee/development/rolling_out_changes_using_feature_flags.html). +This proposal will be shared with Engineering for discussion, feedback, and planning. +The plan for the upcoming milestone must be finalized by the 1st of the month, one week before kickoff on the 8th. -In order to build the final package and present the feature for self-hosted -customers, the feature flag should be removed. This should happen before the -22nd, ideally _at least_ 2 days before. That means MRs with feature -flags being picked at the 19th would have quite a tight schedule, so picking -these _earlier_ is preferable. +## Feature freeze on the 7th for the release on the 22nd -While rare, release managers may decide to reject picking a change into a stable -branch, even when feature flags are used. This might be necessary if the changes -are deemed problematic, too invasive, or there simply isn't enough time to -properly test how the changes behave on GitLab.com. +The feature freeze on the 7th has been discontinued. [Transition period overview](https://gitlab.com/gitlab-org/release/docs/blob/21cbd409dd5f157fe252f254f3e897f01908abe2/general/deploy/auto-deploy-transition.md#transition) +describes the change to this process. During the transition period, the only guarantee that +a change will be included in the release on the 22nd is if the change has been +deployed to GitLab.com prior to this date. ### Between the 1st and the 7th diff --git a/app/assets/javascripts/behaviors/markdown/render_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_mermaid.js index d0b7f3ff7a2..b23de36f860 100644 --- a/app/assets/javascripts/behaviors/markdown/render_mermaid.js +++ b/app/assets/javascripts/behaviors/markdown/render_mermaid.js @@ -59,6 +59,14 @@ export default function renderMermaid($els) { mermaid.init(undefined, el, id => { const svg = document.getElementById(id); + // As of https://github.com/knsv/mermaid/commit/57b780a0d, + // Mermaid will make two init callbacks:one to initialize the + // flow charts, and another to initialize the Gannt charts. + // Guard against an error caused by double initialization. + if (svg.classList.contains('mermaid')) { + return; + } + svg.classList.add('mermaid'); // pre > code > svg diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue index 228bb652597..30971ad5227 100644 --- a/app/assets/javascripts/notes/components/discussion_notes.vue +++ b/app/assets/javascripts/notes/components/discussion_notes.vue @@ -105,8 +105,8 @@ export default { :commit="commit" :help-page-path="helpPagePath" :show-reply-button="userCanReply" - @handle-delete-note="$emit('deleteNote')" - @start-replying="$emit('startReplying')" + @handleDeleteNote="$emit('deleteNote')" + @startReplying="$emit('startReplying')" > <note-edited-text v-if="discussion.resolved" @@ -132,7 +132,7 @@ export default { :note="componentData(note)" :help-page-path="helpPagePath" :line="line" - @handle-delete-note="$emit('deleteNote')" + @handleDeleteNote="$emit('deleteNote')" /> </template> </template> @@ -144,7 +144,7 @@ export default { :note="componentData(note)" :help-page-path="helpPagePath" :line="diffLine" - @handle-delete-note="$emit('deleteNote')" + @handleDeleteNote="$emit('deleteNote')" > <slot v-if="index === 0" slot="avatar-badge" name="avatar-badge"></slot> </component> diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index 7f800d20835..1d8b388e935 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -18,12 +18,12 @@ import UserOverviewBlock from './user_overview_block'; * * <ul class="nav-links"> * <li class="activity-tab active"> - * <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username"> + * <a data-action="activity" data-target="#activity" data-toggle="tab" href="/username"> * Activity * </a> * </li> * <li class="groups-tab"> - * <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups"> + * <a data-action="groups" data-target="#groups" data-toggle="tab" href="/users/username/groups"> * Groups * </a> * </li> diff --git a/app/assets/javascripts/reports/components/report_item.vue b/app/assets/javascripts/reports/components/report_item.vue index 01a30809e1a..2be9c37b00a 100644 --- a/app/assets/javascripts/reports/components/report_item.vue +++ b/app/assets/javascripts/reports/components/report_item.vue @@ -1,6 +1,6 @@ <script> import IssueStatusIcon from '~/reports/components/issue_status_icon.vue'; -import { components, componentNames } from '~/reports/components/issue_body'; +import { components, componentNames } from 'ee_else_ce/reports/components/issue_body'; export default { name: 'ReportItem', diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue index 05ad7710a62..eb0f666422f 100644 --- a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue +++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue @@ -1,6 +1,6 @@ <script> import '~/commons/bootstrap'; -import { GlTooltipDirective } from '@gitlab/ui'; +import { GlTooltip, GlTooltipDirective } from '@gitlab/ui'; import { sprintf } from '~/locale'; import IssueMilestone from '../../components/issue/issue_milestone.vue'; import IssueAssignees from '../../components/issue/issue_assignees.vue'; @@ -13,6 +13,7 @@ export default { IssueMilestone, IssueAssignees, CiIcon, + GlTooltip, }, directives: { GlTooltip: GlTooltipDirective, @@ -24,11 +25,6 @@ export default { required: false, default: false, }, - greyLinkWhenMerged: { - type: Boolean, - required: false, - default: false, - }, }, computed: { stateTitle() { @@ -41,10 +37,12 @@ export default { }, ); }, - issueableLinkClass() { - return this.greyLinkWhenMerged - ? `sortable-link ${this.state === 'merged' ? ' text-secondary' : ''}` - : 'sortable-link'; + heightStyle() { + return { + minHeight: '32px', + width: '0px', + visibility: 'hidden', + }; }, }, }; @@ -56,20 +54,25 @@ export default { 'issuable-info-container': !canReorder, 'card-body': canReorder, }" - class="item-body d-flex align-items-center p-2 p-lg-3 p-xl-2 pl-xl-3" + class="item-body d-flex align-items-center p-2 p-lg-3 py-xl-2 px-xl-3" > <div class="item-contents d-flex align-items-center flex-wrap flex-grow-1 flex-xl-nowrap"> - <div class="item-title d-flex align-items-center mb-1 mb-xl-0"> - <icon - v-if="hasState" - v-tooltip - :css-classes="iconClass" - :name="iconName" - :size="16" - :title="stateTitle" - :aria-label="state" - data-html="true" - /> + <!-- Title area: Status icon (XL) and title --> + <div class="item-title d-flex align-items-center mb-xl-0"> + <span ref="iconElementXL"> + <icon + v-if="hasState" + ref="iconElementXL" + :css-classes="iconClass" + :name="iconName" + :size="16" + :title="stateTitle" + :aria-label="state" + /> + </span> + <gl-tooltip :target="() => $refs.iconElementXL"> + <span v-html="stateTitle"></span> + </gl-tooltip> <icon v-if="confidential" v-gl-tooltip @@ -79,55 +82,81 @@ export default { class="confidential-icon append-right-4 align-self-baseline align-self-md-auto mt-xl-0" :aria-label="__('Confidential')" /> - <a :href="computedPath" :class="issueableLinkClass">{{ title }}</a> + <a :href="computedPath" class="sortable-link">{{ title }}</a> </div> - <div class="item-meta d-flex flex-wrap mt-xl-0 justify-content-xl-end flex-xl-nowrap"> - <div - class="d-flex align-items-center item-path-id order-md-0 mt-md-0 mt-1 ml-xl-2 mr-xl-auto" - > - <icon - v-if="hasState" - v-tooltip - :css-classes="iconClass" - :name="iconName" - :size="16" - :title="stateTitle" - :aria-label="state" - data-html="true" - class="d-xl-none" - /> - <span v-tooltip :title="itemPath" class="path-id-text d-inline-block">{{ - itemPath - }}</span> - {{ pathIdSeparator }}{{ itemId }} - </div> + + <!-- Info area: meta, path, and assignees --> + <div class="item-info-area d-flex flex-xl-grow-1 flex-shrink-0"> + <!-- Meta area: path and attributes --> + <!-- If there is no room beside the path, meta attributes are put ABOVE it (flex-wrap-reverse). --> + <!-- See design: https://gitlab-org.gitlab.io/gitlab-design/hosted/pedro/%2383-issue-mr-rows-cards-spec-previews/#artboard16 --> <div - class="item-meta-child d-flex align-items-center order-0 flex-wrap mr-md-1 ml-md-auto ml-xl-2 flex-xl-nowrap" + class="item-meta d-flex flex-wrap-reverse justify-content-start justify-content-md-between" > - <span v-if="hasPipeline" class="mr-ci-status pr-2"> - <a :href="pipelineStatus.details_path"> - <ci-icon v-gl-tooltip :status="pipelineStatus" :title="pipelineStatusTooltip" /> - </a> - </span> - <issue-milestone - v-if="hasMilestone" - :milestone="milestone" - class="d-flex align-items-center item-milestone" - /> - <slot name="dueDate"></slot> - <slot name="weight"></slot> + <!-- Path area: status icon (<XL), path, issue # --> + <div + class="item-path-area item-path-id d-flex align-items-center mr-2 mt-2 mt-xl-0 ml-xl-2" + > + <span ref="iconElement"> + <icon + v-if="hasState" + :css-classes="iconClass" + :name="iconName" + :title="stateTitle" + :aria-label="state" + data-html="true" + class="d-xl-none" + /> + </span> + <gl-tooltip :target="() => this.$refs.iconElement"> + <span v-html="stateTitle"></span> + </gl-tooltip> + <span v-gl-tooltip :title="itemPath" class="path-id-text d-inline-block">{{ + itemPath + }}</span> + <span>{{ pathIdSeparator }}{{ itemId }}</span> + </div> + + <!-- Attributes area: CI, epic count, weight, milestone --> + <!-- They have a different order on large screen sizes --> + <div class="item-attributes-area d-flex align-items-center mt-2 mt-xl-0"> + <span v-if="hasPipeline" class="mr-ci-status order-md-last"> + <a :href="pipelineStatus.details_path"> + <ci-icon v-gl-tooltip :status="pipelineStatus" :title="pipelineStatusTooltip" /> + </a> + </span> + + <issue-milestone + v-if="hasMilestone" + :milestone="milestone" + class="d-flex align-items-center item-milestone order-md-first ml-md-0" + /> + + <!-- Flex order for slots is defined in the parent component: e.g. related_issues_block.vue --> + <slot name="dueDate"></slot> + <slot name="weight"></slot> + + <issue-assignees + v-if="hasAssignees" + :assignees="assignees" + class="item-assignees align-items-center align-self-end flex-shrink-0 order-md-2 d-none d-md-flex" + /> + </div> </div> + + <!-- Assignees. On small layouts, these are put here, at the end of the card. --> <issue-assignees - v-if="assignees.length" + v-if="assignees.length !== 0" :assignees="assignees" - class="item-assignees d-inline-flex align-items-center align-self-end ml-auto ml-md-0 mb-md-0 order-2 flex-xl-grow-0 mt-xl-0 mr-xl-1" + class="item-assignees d-flex align-items-center align-self-end flex-shrink-0 d-md-none ml-2" /> </div> </div> + <button v-if="canRemove" ref="removeButton" - v-tooltip + v-gl-tooltip :disabled="removeDisabled" type="button" class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button mr-xl-0 align-self-xl-center" @@ -137,5 +166,9 @@ export default { > <icon :size="16" class="btn-item-remove-icon" name="close" /> </button> + + <!-- This element serves to set the issue card's height at a minimum of 32 px. --> + <!-- It fixes #59594: when the remove button is missing, issues have inconsistent heights. --> + <span :style="heightStyle"></span> </div> </template> diff --git a/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js b/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js index 8e0e4baa75a..3c727cb7b3f 100644 --- a/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js +++ b/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js @@ -126,6 +126,9 @@ const mixins = { hasTitle() { return this.title.length > 0; }, + hasAssignees() { + return this.assignees.length > 0; + }, hasMilestone() { return !_.isEmpty(this.milestone); }, diff --git a/app/assets/stylesheets/components/related_items_list.scss b/app/assets/stylesheets/components/related_items_list.scss index 7f9cf1266b1..59224d37744 100644 --- a/app/assets/stylesheets/components/related_items_list.scss +++ b/app/assets/stylesheets/components/related_items_list.scss @@ -83,6 +83,20 @@ $item-weight-max-width: 48px; flex-basis: 100%; } + .item-attributes-area { + > * { + margin-left: 8px; + } + + .board-card-info { + margin-right: 0; + } + + @include media-breakpoint-down(sm) { + margin-left: -8px; + } + } + .item-milestone, .item-weight { cursor: help; @@ -101,39 +115,39 @@ $item-weight-max-width: 48px; .item-weight { max-width: $item-weight-max-width; } +} - .item-assignees { - .user-avatar-link { - margin-right: -$gl-padding-4; - - &:nth-of-type(1) { - z-index: 2; - } +.item-assignees { + .user-avatar-link { + margin-right: -$gl-padding-4; - &:nth-of-type(2) { - z-index: 1; - } + &:nth-of-type(1) { + z-index: 2; + } - &:last-child { - margin-right: 0; - } + &:nth-of-type(2) { + z-index: 1; } - .avatar { - height: $gl-padding; - width: $gl-padding; + &:last-child { margin-right: 0; - vertical-align: bottom; } + } - .avatar-counter { - height: $gl-padding; - border: 1px solid transparent; - background-color: $gl-text-color-tertiary; - font-weight: $gl-font-weight-bold; - padding: 0 $gl-padding-4; - line-height: $gl-padding; - } + .avatar { + height: $gl-padding; + width: $gl-padding; + margin-right: 0; + vertical-align: bottom; + } + + .avatar-counter { + height: $gl-padding; + border: 1px solid transparent; + background-color: $gl-text-color-tertiary; + font-weight: $gl-font-weight-bold; + padding: 0 $gl-padding-4; + line-height: $gl-padding; } } @@ -150,12 +164,6 @@ $item-weight-max-width: 48px; .issue-token-state-icon-closed { display: block; } - - @include media-breakpoint-down(sm) { - &:not(.mr-item-path) { - order: 1; - } - } } .btn-item-remove { @@ -179,6 +187,10 @@ $item-weight-max-width: 48px; } @include media-breakpoint-up(sm) { + .item-info-area { + flex-basis: 100%; + } + .sortable-link { max-width: 90%; } @@ -241,7 +253,8 @@ $item-weight-max-width: 48px; .item-title { min-width: 0; width: auto; - flex-basis: unset; + flex-basis: auto; + flex-shrink: 1; font-weight: $gl-font-weight-normal; .issue-token-state-icon-open, @@ -250,6 +263,10 @@ $item-weight-max-width: 48px; margin-right: $gl-padding-8; } } + + .item-info-area { + flex-basis: auto; + } } .item-contents { diff --git a/app/assets/stylesheets/components/toast.scss b/app/assets/stylesheets/components/toast.scss index 33e1c4e5349..acbd909d595 100644 --- a/app/assets/stylesheets/components/toast.scss +++ b/app/assets/stylesheets/components/toast.scss @@ -21,7 +21,7 @@ background-color: rgba($gray-900, $toast-background-opacity); @include media-breakpoint-down(xs) { - .action:first-child { + .action:first-of-type { // Ensures actions buttons are right aligned on mobile margin-left: auto; } @@ -33,7 +33,7 @@ text-transform: none; font-size: $gl-font-size; - &:first-child { + &:first-of-type { padding-right: 0; } } diff --git a/app/assets/stylesheets/framework/system_messages.scss b/app/assets/stylesheets/framework/system_messages.scss index 6205ccaa52f..5c298d5a588 100644 --- a/app/assets/stylesheets/framework/system_messages.scss +++ b/app/assets/stylesheets/framework/system_messages.scss @@ -98,14 +98,4 @@ top: auto; bottom: auto; } - - .content-wrapper { - .with-system-header & { - margin-top: 0; - } - - .with-system-footer & { - margin-top: 0; - } - } } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7321f719deb..75108bf2646 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -516,4 +516,10 @@ class ApplicationController < ActionController::Base def sentry_context Gitlab::Sentry.context(current_user) end + + def allow_gitaly_ref_name_caching + ::Gitlab::GitalyClient.allow_ref_name_caching do + yield + end + end end diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb index 7625600e452..9da2f888ead 100644 --- a/app/controllers/concerns/boards_responses.rb +++ b/app/controllers/concerns/boards_responses.rb @@ -3,8 +3,9 @@ module BoardsResponses include Gitlab::Utils::StrongMemoize + # Overridden on EE module def board_params - params.require(:board).permit(:name, :weight, :milestone_id, :assignee_id, label_ids: []) + params.require(:board).permit(:name) end def parent diff --git a/app/controllers/concerns/multiple_boards_actions.rb b/app/controllers/concerns/multiple_boards_actions.rb new file mode 100644 index 00000000000..95a6800f55c --- /dev/null +++ b/app/controllers/concerns/multiple_boards_actions.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +module MultipleBoardsActions + include Gitlab::Utils::StrongMemoize + extend ActiveSupport::Concern + + included do + include BoardsActions + + before_action :redirect_to_recent_board, only: [:index] + before_action :authenticate_user!, only: [:recent] + before_action :authorize_create_board!, only: [:create] + before_action :authorize_admin_board!, only: [:create, :update, :destroy] + end + + def recent + recent_visits = ::Boards::VisitsFinder.new(parent, current_user).latest(4) + recent_boards = recent_visits.map(&:board) + + render json: serialize_as_json(recent_boards) + end + + def create + board = Boards::CreateService.new(parent, current_user, board_params).execute + + respond_to do |format| + format.json do + if board.persisted? + extra_json = { board_path: board_path(board) } + render json: serialize_as_json(board).merge(extra_json) + else + render json: board.errors, status: :unprocessable_entity + end + end + end + end + + def update + service = Boards::UpdateService.new(parent, current_user, board_params) + + respond_to do |format| + format.json do + if service.execute(board) + extra_json = { board_path: board_path(board) } + render json: serialize_as_json(board).merge(extra_json) + else + render json: board.errors, status: :unprocessable_entity + end + end + end + end + + def destroy + service = Boards::DestroyService.new(parent, current_user) + service.execute(board) + + respond_to do |format| + format.json { head :ok } + format.html { redirect_to boards_path, status: :found } + end + end + + private + + def redirect_to_recent_board + return if request.format.json? || !parent.multiple_issue_boards_available? || !latest_visited_board + + redirect_to board_path(latest_visited_board.board) + end + + def latest_visited_board + @latest_visited_board ||= Boards::VisitsFinder.new(parent, current_user).latest + end + + def authorize_create_board! + check_multiple_group_issue_boards_available! if group? + end + + def authorize_admin_board! + return render_404 unless can?(current_user, :admin_board, parent) + end + + def serializer + BoardSerializer.new(current_user: current_user) + end + + def serialize_as_json(resource) + serializer.represent(resource, serializer: 'board', include_full_project_path: board.group_board?) + end +end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 80e4f54bbf4..90396c15375 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -87,10 +87,4 @@ class Projects::ApplicationController < ApplicationController def check_issues_available! return render_404 unless @project.feature_available?(:issues, current_user) end - - def allow_gitaly_ref_name_caching - ::Gitlab::GitalyClient.allow_ref_name_caching do - yield - end - end end diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index 95897aaf980..14b02993e6e 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Projects::BoardsController < Projects::ApplicationController - include BoardsActions + include MultipleBoardsActions include IssuableCollections before_action :check_issues_available! diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index a80ab3bcd28..8c674be58c5 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -5,6 +5,8 @@ class SearchController < ApplicationController include SearchHelper include RendersCommits + around_action :allow_gitaly_ref_name_caching + skip_before_action :authenticate_user! requires_cross_project_access if: -> do search_term_present = params[:search].present? || params[:term].present? diff --git a/app/finders/boards/visits_finder.rb b/app/finders/boards/visits_finder.rb new file mode 100644 index 00000000000..d17a27f72dc --- /dev/null +++ b/app/finders/boards/visits_finder.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Boards + class VisitsFinder + attr_accessor :params, :current_user, :parent + + def initialize(parent, current_user) + @current_user = current_user + @parent = parent + end + + def execute(count = nil) + return unless current_user + + recent_visit_model.latest(current_user, parent, count: count) + end + + alias_method :latest, :execute + + private + + def recent_visit_model + parent.is_a?(Group) ? BoardGroupRecentVisit : BoardProjectRecentVisit + end + end +end diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index c5130b430b9..8ef68018d23 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -15,7 +15,8 @@ module BoardsHelper root_path: root_path, bulk_update_path: @bulk_issues_path, default_avatar: image_path(default_avatar), - time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s + time_tracking_limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s, + recent_boards_endpoint: recent_boards_path } end @@ -87,6 +88,18 @@ module BoardsHelper end def boards_link_text - s_("IssueBoards|Board") + if current_board_parent.multiple_issue_boards_available? + s_("IssueBoards|Boards") + else + s_("IssueBoards|Board") + end + end + + def recent_boards_path + recent_project_boards_path(@project) if current_board_parent.is_a?(Project) + end + + def current_board_json + board.to_json end end diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb index 5afb193cf86..9296c28776b 100644 --- a/app/models/clusters/platforms/kubernetes.rb +++ b/app/models/clusters/platforms/kubernetes.rb @@ -4,7 +4,6 @@ module Clusters module Platforms class Kubernetes < ApplicationRecord include Gitlab::Kubernetes - include ReactiveCaching include EnumWithNil include AfterCommitQueue @@ -46,8 +45,6 @@ module Clusters validate :prevent_modification, on: :update - after_save :clear_reactive_cache! - alias_attribute :ca_pem, :ca_cert delegate :enabled?, to: :cluster, allow_nil: true @@ -96,27 +93,16 @@ module Clusters end end - # Constructs a list of terminals from the reactive cache - # - # Returns nil if the cache is empty, in which case you should try again a - # short time later - def terminals(environment) - with_reactive_cache do |data| - project = environment.project - - pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug) - terminals = pods.flat_map { |pod| terminals_for_pod(api_url, cluster.kubernetes_namespace_for(project), pod) }.compact - terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) } - end - end - - # Caches resources in the namespace so other calls don't need to block on - # network access - def calculate_reactive_cache + def calculate_reactive_cache_for(environment) return unless enabled? - # We may want to cache extra things in the future - { pods: read_pods } + { pods: read_pods(environment.deployment_namespace) } + end + + def terminals(environment, data) + pods = filter_by_project_environment(data[:pods], environment.project.full_path_slug, environment.slug) + terminals = pods.flat_map { |pod| terminals_for_pod(api_url, environment.deployment_namespace, pod) }.compact + terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) } end def kubeclient @@ -133,6 +119,12 @@ module Clusters ca_pem: ca_pem) end + def read_pods(namespace) + kubeclient.get_pods(namespace: namespace).as_json + rescue Kubeclient::ResourceNotFoundError + [] + end + def build_kube_client! raise "Incomplete settings" unless api_url @@ -148,19 +140,6 @@ module Clusters ) end - # Returns a hash of all pods in the namespace - def read_pods - # TODO: The project lookup here should be moved (to environment?), - # which will enable reading pods from the correct namespace for group - # and instance clusters. - # This will be done in https://gitlab.com/gitlab-org/gitlab-ce/issues/61156 - return [] unless cluster.project_type? - - kubeclient.get_pods(namespace: cluster.kubernetes_namespace_for(cluster.first_project)).as_json - rescue Kubeclient::ResourceNotFoundError - [] - end - def kubeclient_ssl_options opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER } diff --git a/app/models/environment.rb b/app/models/environment.rb index 1f7e8815c8e..b8ee54c1696 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -2,6 +2,8 @@ class Environment < ApplicationRecord include Gitlab::Utils::StrongMemoize + include ReactiveCaching + # Used to generate random suffixes for the slug LETTERS = ('a'..'z').freeze NUMBERS = ('0'..'9').freeze @@ -17,6 +19,7 @@ class Environment < ApplicationRecord before_validation :generate_slug, if: ->(env) { env.slug.blank? } before_save :set_environment_type + after_save :clear_reactive_cache! validates :name, presence: true, @@ -159,7 +162,21 @@ class Environment < ApplicationRecord end def terminals - deployment_platform.terminals(self) if has_terminals? + with_reactive_cache do |data| + deployment_platform.terminals(self, data) + end + end + + def calculate_reactive_cache + return unless has_terminals? && !project.pending_delete? + + deployment_platform.calculate_reactive_cache_for(self) + end + + def deployment_namespace + strong_memoize(:kubernetes_namespace) do + deployment_platform&.kubernetes_namespace_for(project) + end end def has_metrics? diff --git a/app/models/postgresql/replication_slot.rb b/app/models/postgresql/replication_slot.rb index 74ccf23cf69..7a123deb719 100644 --- a/app/models/postgresql/replication_slot.rb +++ b/app/models/postgresql/replication_slot.rb @@ -28,7 +28,7 @@ module Postgresql # We force the use of a transaction here so the query always goes to the # primary, even when using the EE DB load balancer. sizes = transaction { pluck(lag_function) } - too_great = sizes.count { |size| size >= max } + too_great = sizes.compact.count { |size| size >= max } # If too many replicas are falling behind too much, the availability of a # GitLab instance might suffer. To prevent this from happening we require diff --git a/app/models/project.rb b/app/models/project.rb index 351d08eaf63..c642b65f674 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1949,9 +1949,8 @@ class Project < ApplicationRecord end end - # Overridden on EE module def multiple_issue_boards_available? - false + true end def full_path_before_last_save diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 08bfe5d14ee..3c9ffbb2065 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -196,6 +196,7 @@ class ProjectPolicy < BasePolicy rule { guest & can?(:read_container_image) }.enable :build_read_container_image rule { can?(:reporter_access) }.policy do + enable :admin_board enable :download_code enable :read_statistics enable :download_wiki_code @@ -240,6 +241,7 @@ class ProjectPolicy < BasePolicy rule { can?(:developer_access) & can?(:create_issue) }.enable :import_issues rule { can?(:developer_access) }.policy do + enable :admin_board enable :admin_merge_request enable :admin_milestone enable :update_merge_request @@ -266,6 +268,7 @@ class ProjectPolicy < BasePolicy end rule { can?(:maintainer_access) }.policy do + enable :admin_board enable :push_to_delete_protected_branch enable :update_project_snippet enable :update_environment diff --git a/app/serializers/board_simple_entity.rb b/app/serializers/board_simple_entity.rb index f297d993e27..029d3808e75 100644 --- a/app/serializers/board_simple_entity.rb +++ b/app/serializers/board_simple_entity.rb @@ -2,4 +2,5 @@ class BoardSimpleEntity < Grape::Entity expose :id + expose :name end diff --git a/app/services/boards/create_service.rb b/app/services/boards/create_service.rb index 1b796cef3e2..dd9358913fd 100644 --- a/app/services/boards/create_service.rb +++ b/app/services/boards/create_service.rb @@ -9,7 +9,7 @@ module Boards private def can_create_board? - parent.boards.empty? + parent.boards.empty? || parent.multiple_issue_boards_available? end def create_board! diff --git a/app/services/boards/destroy_service.rb b/app/services/boards/destroy_service.rb new file mode 100644 index 00000000000..ea0c1394aa3 --- /dev/null +++ b/app/services/boards/destroy_service.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Boards + class DestroyService < Boards::BaseService + def execute(board) + return false if parent.boards.size == 1 + + board.destroy + end + end +end diff --git a/app/services/boards/update_service.rb b/app/services/boards/update_service.rb new file mode 100644 index 00000000000..88aced01ccd --- /dev/null +++ b/app/services/boards/update_service.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Boards + class UpdateService < Boards::BaseService + def execute(board) + board.update(params) + end + end +end diff --git a/app/services/boards/visits/latest_service.rb b/app/services/boards/visits/latest_service.rb deleted file mode 100644 index d13e25b4f12..00000000000 --- a/app/services/boards/visits/latest_service.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module Boards - module Visits - class LatestService < Boards::BaseService - def execute - return unless current_user - - recent_visit_model.latest(current_user, parent, count: params[:count]) - end - - private - - def recent_visit_model - parent.is_a?(Group) ? BoardGroupRecentVisit : BoardProjectRecentVisit - end - end - end -end diff --git a/app/views/layouts/fullscreen.html.haml b/app/views/layouts/fullscreen.html.haml index e29f646ed4f..fa04b5be9f2 100644 --- a/app/views/layouts/fullscreen.html.haml +++ b/app/views/layouts/fullscreen.html.haml @@ -10,5 +10,5 @@ = render "layouts/broadcast" = yield :flash_message = render "layouts/flash" - .content-wrapper{ id: "content-body", class: "d-flex flex-column align-items-stretch" } + .content-wrapper{ id: "content-body", class: "d-flex flex-column align-items-stretch mt-0" } = yield diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml index 044adb75bea..407de590efb 100644 --- a/app/views/projects/notes/_actions.html.haml +++ b/app/views/projects/notes/_actions.html.haml @@ -39,7 +39,7 @@ - if can?(current_user, :award_emoji, note) - if note.emoji_awardable? .note-actions-item - = button_tag title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji} has-tooltip btn btn-transparent", data: { position: 'right', container: 'body' } do + = button_tag title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip btn btn-transparent", data: { position: 'right', container: 'body' } do = icon('spinner spin') %span{ class: 'link-highlight award-control-icon-neutral' }= sprite_icon('slight-smile') %span{ class: 'link-highlight award-control-icon-positive' }= sprite_icon('smiley') diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 13847cd9be1..576ec3e1782 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -28,7 +28,7 @@ .js-projects-list-holder - if any_projects?(projects) - - load_pipeline_status(projects) + - load_pipeline_status(projects) if pipeline_status %ul.projects-list{ class: css_classes } - projects.each_with_index do |project, i| - css_class = (i >= projects_limit) || project.pending_delete? ? 'hide' : nil diff --git a/changelogs/unreleased/53811-issue-boards-to-core-projects-backend-ce.yml b/changelogs/unreleased/53811-issue-boards-to-core-projects-backend-ce.yml new file mode 100644 index 00000000000..d8fbd7ec362 --- /dev/null +++ b/changelogs/unreleased/53811-issue-boards-to-core-projects-backend-ce.yml @@ -0,0 +1,5 @@ +--- +title: Move Multiple Issue Boards for Projects to Core +merge_request: +author: +type: added diff --git a/changelogs/unreleased/54595-incorrect-reaction-emoji-placement-in-discussion.yml b/changelogs/unreleased/54595-incorrect-reaction-emoji-placement-in-discussion.yml new file mode 100644 index 00000000000..639eefb50cb --- /dev/null +++ b/changelogs/unreleased/54595-incorrect-reaction-emoji-placement-in-discussion.yml @@ -0,0 +1,5 @@ +--- +title: Fix incorrect emoji placement in commit diff discussion +merge_request: 29445 +author: +type: fixed diff --git a/changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml b/changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml new file mode 100644 index 00000000000..0b8d301352b --- /dev/null +++ b/changelogs/unreleased/61156-instance-level-cluster-pod-terminal-access.yml @@ -0,0 +1,5 @@ +--- +title: Enable terminals for instance and group clusters +merge_request: 28613 +author: +type: added diff --git a/changelogs/unreleased/63200-reply-button-broken.yml b/changelogs/unreleased/63200-reply-button-broken.yml new file mode 100644 index 00000000000..11f81dbd925 --- /dev/null +++ b/changelogs/unreleased/63200-reply-button-broken.yml @@ -0,0 +1,5 @@ +--- +title: Fix unresponsive reply button in discussions +merge_request: 29936 +author: +type: fixed diff --git a/changelogs/unreleased/add-metrics-dashboard-permission-check.yml b/changelogs/unreleased/add-metrics-dashboard-permission-check.yml new file mode 100644 index 00000000000..0ea2c4c8e41 --- /dev/null +++ b/changelogs/unreleased/add-metrics-dashboard-permission-check.yml @@ -0,0 +1,5 @@ +--- +title: Add permission check to metrics dashboards endpoint +merge_request: 30017 +author: +type: added diff --git a/changelogs/unreleased/dz-remove-deprecated-user-routes.yml b/changelogs/unreleased/dz-remove-deprecated-user-routes.yml new file mode 100644 index 00000000000..92c2e39dd20 --- /dev/null +++ b/changelogs/unreleased/dz-remove-deprecated-user-routes.yml @@ -0,0 +1,5 @@ +--- +title: Remove depreated /u/:username routing +merge_request: 30044 +author: +type: removed diff --git a/changelogs/unreleased/po-raw-changes-encoding.yml b/changelogs/unreleased/po-raw-changes-encoding.yml new file mode 100644 index 00000000000..051d18f50c7 --- /dev/null +++ b/changelogs/unreleased/po-raw-changes-encoding.yml @@ -0,0 +1,5 @@ +--- +title: Expect bytes from Gitaly RPC GetRawChanges +merge_request: 28164 +author: +type: fixed diff --git a/changelogs/unreleased/sh-add-gitaly-ref-caching-search-controller.yml b/changelogs/unreleased/sh-add-gitaly-ref-caching-search-controller.yml new file mode 100644 index 00000000000..d4be28e9883 --- /dev/null +++ b/changelogs/unreleased/sh-add-gitaly-ref-caching-search-controller.yml @@ -0,0 +1,5 @@ +--- +title: Enable Gitaly ref caching for SearchController +merge_request: 30105 +author: +type: performance diff --git a/changelogs/unreleased/sh-avoid-loading-pipeline-status.yml b/changelogs/unreleased/sh-avoid-loading-pipeline-status.yml new file mode 100644 index 00000000000..2dead948786 --- /dev/null +++ b/changelogs/unreleased/sh-avoid-loading-pipeline-status.yml @@ -0,0 +1,5 @@ +--- +title: Avoid loading pipeline status in search results +merge_request: 30111 +author: +type: performance diff --git a/changelogs/unreleased/sh-handle-nil-replication-lag.yml b/changelogs/unreleased/sh-handle-nil-replication-lag.yml new file mode 100644 index 00000000000..5638d7e79e3 --- /dev/null +++ b/changelogs/unreleased/sh-handle-nil-replication-lag.yml @@ -0,0 +1,5 @@ +--- +title: Fix background migrations failing with unused replication slot +merge_request: 30042 +author: +type: fixed diff --git a/changelogs/unreleased/sh-update-mermaid.yml b/changelogs/unreleased/sh-update-mermaid.yml new file mode 100644 index 00000000000..9a7726cf716 --- /dev/null +++ b/changelogs/unreleased/sh-update-mermaid.yml @@ -0,0 +1,5 @@ +--- +title: Update Mermaid to 8.1.0 +merge_request: 30036 +author: +type: fixed diff --git a/config/routes.rb b/config/routes.rb index cb90a0134c4..a42fc037227 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,10 +27,16 @@ Rails.application.routes.draw do authorizations: 'oauth/authorizations' end - # This is here so we can "reserve" the path for the Jira integration in GitLab EE - # Having a non-existent controller here does not affect the scope in any way since all possible routes - # get a 404 proc returned. It is written in this way to minimize merge conflicts with EE + # This prefixless path is required because Jira gets confused if we set it up with a path + # More information: https://gitlab.com/gitlab-org/gitlab-ee/issues/6752 scope path: '/login/oauth', controller: 'oauth/jira/authorizations', as: :oauth_jira do + Gitlab.ee do + get :authorize, action: :new + get :callback + post :access_token + end + + # This helps minimize merge conflicts with CE for this scope block match '*all', via: [:get, :post], to: proc { [404, {}, ['']] } end @@ -45,6 +51,10 @@ Rails.application.routes.draw do get '/autocomplete/award_emojis' => 'autocomplete#award_emojis' get '/autocomplete/merge_request_target_branches' => 'autocomplete#merge_request_target_branches' + Gitlab.ee do + get '/autocomplete/project_groups' => 'autocomplete#project_groups' + end + # Search get 'search' => 'search#show' get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete @@ -73,6 +83,11 @@ Rails.application.routes.draw do end resources :issues, module: :boards, only: [:index, :update] + + Gitlab.ee do + resources :users, module: :boards, only: [:index] + resources :milestones, module: :boards, only: [:index] + end end get 'acme-challenge/' => 'acme_challenges#show' @@ -86,6 +101,11 @@ Rails.application.routes.draw do draw :operations draw :instance_statistics + Gitlab.ee do + draw :smartcard + draw :jira_connect + end + if ENV['GITLAB_ENABLE_CHAOS_ENDPOINTS'] get '/chaos/leakmem' => 'chaos#leakmem' get '/chaos/cpuspin' => 'chaos#cpuspin' @@ -102,6 +122,10 @@ Rails.application.routes.draw do end member do + Gitlab.ee do + get :metrics, format: :json + end + scope :applications do post '/:application', to: 'clusters/applications#create', as: :install_applications patch '/:application', to: 'clusters/applications#update', as: :update_applications diff --git a/config/routes/api.rb b/config/routes/api.rb index 3719b7d3a1e..3ba9176d943 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -3,5 +3,5 @@ constraints(::Constraints::FeatureConstrainer.new(:graphql, default_enabled: tru mount GraphiQL::Rails::Engine, at: '/-/graphql-explorer', graphql_path: '/api/graphql' end -API::API.logger Rails.logger -mount API::API => '/' +::API::API.logger Rails.logger +mount ::API::API => '/' diff --git a/config/routes/profile.rb b/config/routes/profile.rb index 0e213b0b989..83a2b33514b 100644 --- a/config/routes/profile.rb +++ b/config/routes/profile.rb @@ -40,6 +40,15 @@ resource :profile, only: [:show, :update] do put :resend_confirmation_instructions end end + + Gitlab.ee do + resource :slack, only: [:edit] do + member do + get :slack_link + end + end + end + resources :chat_names, only: [:index, :new, :create, :destroy] do collection do delete :deny @@ -63,5 +72,10 @@ resource :profile, only: [:show, :update] do end resources :u2f_registrations, only: [:destroy] + + Gitlab.ee do + resources :pipeline_quota, only: [:index] + resources :billings, only: [:index] + end end end diff --git a/config/routes/project.rb b/config/routes/project.rb index 0e8e089c78a..bcbbd7222e0 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -79,12 +79,22 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resource :operations, only: [:show, :update] resource :integrations, only: [:show] + Gitlab.ee do + resource :slack, only: [:destroy, :edit, :update] do + get :slack_auth + end + end + resource :repository, only: [:show], controller: :repository do post :create_deploy_token, path: 'deploy_token/create' post :cleanup end end + Gitlab.ee do + resources :feature_flags + end + resources :autocomplete_sources, only: [] do collection do get 'members' @@ -155,7 +165,11 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end - resources :boards, only: [:index, :show], constraints: { id: /\d+/ } + resources :boards, only: [:index, :show, :create, :update, :destroy], constraints: { id: /\d+/ } do + collection do + get :recent + end + end resources :releases, only: [:index] resources :forks, only: [:index, :new, :create] resources :group_links, only: [:index, :create, :update, :destroy], constraints: { id: /\d+/ } @@ -199,8 +213,18 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resource :mattermost, only: [:new, :create] namespace :prometheus do - resources :metrics, constraints: { id: %r{[^\/]+} }, only: [] do + resources :metrics, constraints: { id: %r{[^\/]+} }, only: [:index, :new, :create, :edit, :update, :destroy] do get :active_common, on: :collection + + Gitlab.ee do + post :validate_query, on: :collection + end + end + + Gitlab.ee do + resources :alerts, constraints: { id: /\d+/ }, only: [:index, :create, :show, :update, :destroy] do + post :notify, on: :collection + end end end @@ -212,6 +236,15 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do get :pipeline_status get :ci_environments_status post :toggle_subscription + + Gitlab.ee do + get :approvals + post :approvals, action: :approve + delete :approvals, action: :unapprove + + post :rebase + end + post :remove_wip post :assign_related_issues get :discussions, format: :json @@ -244,6 +277,21 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do post :bulk_update end + Gitlab.ee do + resources :approvers, only: :destroy + delete 'approvers', to: 'approvers#destroy_via_user_id', as: :approver_via_user_id + resources :approver_groups, only: :destroy + + scope module: :merge_requests do + resources :drafts, only: [:index, :update, :create, :destroy] do + collection do + post :publish + delete :discard + end + end + end + end + resources :discussions, only: [:show], constraints: { id: /\h{40}/ } do member do post :resolve @@ -274,6 +322,17 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end + Gitlab.ee do + resources :path_locks, only: [:index, :destroy] do + collection do + post :toggle + end + end + + get '/service_desk' => 'service_desk#show', as: :service_desk + put '/service_desk' => 'service_desk#update', as: :service_desk_refresh + end + resource :variables, only: [:show, :update] resources :triggers, only: [:index, :create, :edit, :update, :destroy] do @@ -289,6 +348,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end + Gitlab.ee do + resources :push_rules, constraints: { id: /\d+/ }, only: [:update] + end + resources :pipelines, only: [:index, :new, :create, :show] do collection do resource :pipelines_settings, path: 'settings', only: [:show, :update] @@ -303,6 +366,11 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do get :builds get :failures get :status + + Gitlab.ee do + get :security + get :licenses + end end member do @@ -331,6 +399,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil } get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#proxy', as: :prometheus_api + + Gitlab.ee do + get :logs + end end collection do @@ -347,6 +419,14 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end + Gitlab.ee do + resources :protected_environments, only: [:create, :update, :destroy], constraints: { id: /\d+/ } do + collection do + get 'search' + end + end + end + resource :cycle_analytics, only: [:show] namespace :cycle_analytics do @@ -399,6 +479,14 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end + Gitlab.ee do + namespace :security do + resource :dashboard, only: [:show], controller: :dashboard + end + + resources :vulnerability_feedback, only: [:index, :create, :update, :destroy], constraints: { id: /\d+/ } + end + get :issues, to: 'issues#calendar', constraints: lambda { |req| req.format == :ics } resources :issues, concerns: :awardable, constraints: { id: /\d+/ } do @@ -417,6 +505,15 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do collection do post :bulk_update post :import_csv + + Gitlab.ee do + post :export_csv + get :service_desk + end + end + + Gitlab.ee do + resources :issue_links, only: [:index, :create, :destroy], as: 'links', path: 'links' end end @@ -451,6 +548,11 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end + Gitlab.ee do + resources :approvers, only: :destroy + resources :approver_groups, only: :destroy + end + resources :runner_projects, only: [:create, :destroy] resources :badges, only: [:index] do collection do @@ -465,6 +567,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end + Gitlab.ee do + resources :audit_events, only: [:index] + end + resources :error_tracking, only: [:index], controller: :error_tracking do collection do post :list_projects @@ -475,6 +581,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do # its preferable to keep it below all other project routes draw :wiki draw :repository + + Gitlab.ee do + resources :managed_licenses, only: [:index, :show, :new, :create, :edit, :update, :destroy] + end end resources(:projects, diff --git a/config/routes/repository.rb b/config/routes/repository.rb index b96315bfe8b..1ea0ae72614 100644 --- a/config/routes/repository.rb +++ b/config/routes/repository.rb @@ -58,7 +58,7 @@ scope format: false do resource :release, controller: 'tags/releases', only: [:edit, :update] end - resources :protected_branches, only: [:index, :show, :create, :update, :destroy] + resources :protected_branches, only: [:index, :show, :create, :update, :destroy, :patch], constraints: { id: Gitlab::PathRegex.git_reference_regex } resources :protected_tags, only: [:index, :show, :create, :update, :destroy] end diff --git a/config/routes/snippets.rb b/config/routes/snippets.rb index 81bc890d86b..ba6da3ac57e 100644 --- a/config/routes/snippets.rb +++ b/config/routes/snippets.rb @@ -17,5 +17,5 @@ resources :snippets, concerns: :awardable do end end -get '/s/:username', to: redirect('u/%{username}/snippets'), +get '/s/:username', to: redirect('users/%{username}/snippets'), constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ } diff --git a/config/routes/user.rb b/config/routes/user.rb index e0ae264e2c0..80f266aa8f9 100644 --- a/config/routes/user.rb +++ b/config/routes/user.rb @@ -1,3 +1,8 @@ +Gitlab.ee do + get 'unsubscribes/:email', to: 'unsubscribes#show', as: :unsubscribe + post 'unsubscribes/:email', to: 'unsubscribes#create' +end + # Allows individual providers to be directed to a chosen controller # Call from inside devise_scope def override_omniauth(provider, controller, path_prefix = '/users/auth') @@ -25,6 +30,17 @@ devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, devise_scope :user do get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error get '/users/almost_there' => 'confirmations#almost_there' + + Gitlab.ee do + get '/users/auth/kerberos_spnego/negotiate' => 'omniauth_kerberos_spnego#negotiate' + end +end + +scope '-/users', module: :users do + resources :terms, only: [:index] do + post :accept, on: :member + post :decline, on: :member + end end scope '-/users', module: :users do @@ -48,15 +64,6 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d get :activity get '/', to: redirect('%{username}'), as: nil end - - # Compatibility with old routing - # TODO (dzaporozhets): remove in 10.0 - get '/u/:username', to: redirect('%{username}') - # TODO (dzaporozhets): remove in 9.0 - get '/u/:username/groups', to: redirect('users/%{username}/groups') - get '/u/:username/projects', to: redirect('users/%{username}/projects') - get '/u/:username/snippets', to: redirect('users/%{username}/snippets') - get '/u/:username/contributed', to: redirect('users/%{username}/contributed') end constraints(::Constraints::UserUrlConstrainer.new) do diff --git a/danger/only_documentation/Dangerfile b/danger/only_documentation/Dangerfile new file mode 100644 index 00000000000..8e4564f22b6 --- /dev/null +++ b/danger/only_documentation/Dangerfile @@ -0,0 +1,24 @@ +# rubocop:disable Style/SignalException +# frozen_string_literal: true + +has_only_docs_changes = helper.all_changed_files.all? { |file| file.start_with?('doc/') } +is_docs_only_branch = gitlab.branch_for_head =~ /(^docs[\/-].*|.*-docs$)/ + +if is_docs_only_branch && !has_only_docs_changes + fail "It seems like your branch name has a `docs` prefix or suffix. "\ + "The CI won't run the full pipeline, but you also have changed non-docs files. "\ + "Please recreate this MR with a new branch name." +end + +if has_only_docs_changes && !is_docs_only_branch + markdown(<<~MARKDOWN) + + ## Documentation only changes + + Hey! Seems your merge request contains only docs changes. + Tired of waiting 2 hours for the pipeline to finish? + Next time, prepend `docs-` to [your branch name](https://docs.gitlab.com/ee/development/documentation/#branch-naming) + and the pipeline will finish before you say GitLab (x300)! + + MARKDOWN +end diff --git a/doc/README.md b/doc/README.md index 3863e17c268..cfda2c9293d 100644 --- a/doc/README.md +++ b/doc/README.md @@ -54,7 +54,7 @@ GitLab provides solutions for [all the stages of the DevOps lifecycle](https://a ![DevOps Stages](img/devops-stages.png) GitLab is like a top-of-the-line kitchen for making software. As the executive -chef, you decide what software you want serve. Using your recipe, GitLab handles +chef, you decide what software you want to serve. Using your recipe, GitLab handles all the prep work, cooking, and delivery, so you can turn around orders faster than ever. diff --git a/doc/administration/high_availability/gitaly.md b/doc/administration/high_availability/gitaly.md index 90e5f71d835..b7eaa4ce105 100644 --- a/doc/administration/high_availability/gitaly.md +++ b/doc/administration/high_availability/gitaly.md @@ -2,13 +2,13 @@ Gitaly does not yet support full high availability. However, Gitaly is quite stable and is in use on GitLab.com. Scaled and highly available GitLab environments -should consider using Gitaly on a separate node. +should consider using Gitaly on a separate node. -See the [Gitaly HA Epic](https://gitlab.com/groups/gitlab-org/-/epics/289) to -track plans and progress toward high availability support. +See the [Gitaly HA Epic](https://gitlab.com/groups/gitlab-org/-/epics/289) to +track plans and progress toward high availability support. This document is relevant for [Scaled Architecture](README.md#scalable-architecture-examples) -environments and [High Availability Architecture](README.md#high-availability-architecture-examples). +environments and [High Availability Architecture](README.md#high-availability-architecture-examples). ## Running Gitaly on its own server @@ -24,23 +24,25 @@ Continue configuration of other components by going back to: > [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/3786) in GitLab 12.0. - 1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration: +1. Make sure to collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z` - ```ruby - # Enable service discovery for Prometheus - consul['enable'] = true - consul['monitoring_service_discovery'] = true +1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration: - # Replace placeholders - # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z - # with the addresses of the Consul server nodes - consul['configuration'] = { - retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z), - } + ```ruby + # Enable service discovery for Prometheus + consul['enable'] = true + consul['monitoring_service_discovery'] = true - # Set the network addresses that the exporters will listen on - node_exporter['listen_address'] = '0.0.0.0:9100' - gitaly['prometheus_listen_addr'] = "0.0.0.0:9236" - ``` + # Replace placeholders + # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z + # with the addresses of the Consul server nodes + consul['configuration'] = { + retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z), + } - 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. + # Set the network addresses that the exporters will listen on + node_exporter['listen_address'] = '0.0.0.0:9100' + gitaly['prometheus_listen_addr'] = "0.0.0.0:9236" + ``` + +1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md index b1126881440..3045be616a6 100644 --- a/doc/administration/high_availability/gitlab.md +++ b/doc/administration/high_availability/gitlab.md @@ -138,6 +138,8 @@ need some extra configuration. If you enable Monitoring, it must be enabled on **all** GitLab servers. +1. Make sure to collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z` + 1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration: ```ruby diff --git a/doc/administration/high_availability/monitoring_node.md b/doc/administration/high_availability/monitoring_node.md index d16bf7dc0f0..ef415dde10a 100644 --- a/doc/administration/high_availability/monitoring_node.md +++ b/doc/administration/high_availability/monitoring_node.md @@ -16,6 +16,8 @@ Omnibus: package you want using **steps 1 and 2** from the GitLab downloads page. - Do not complete any other steps on the download page. +1. Make sure to collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z` + 1. Edit `/etc/gitlab/gitlab.rb` and add the contents: ```ruby diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index f61a8834af3..8621224272c 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -22,10 +22,10 @@ environments including [Basic Scaling](README.md#basic-scaling) and ### Provide your own Redis instance **[CORE ONLY]** -If you want to use your own deployed Redis instance(s), -see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only) -for more details. However, you can use the GitLab Omnibus package to easily -deploy the bundled Redis. +If you want to use your own deployed Redis instance(s), +see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only) +for more details. However, you can use the GitLab Omnibus package to easily +deploy the bundled Redis. ### Standalone Redis using GitLab Omnibus **[CORE ONLY]** @@ -62,11 +62,11 @@ Omnibus: pgbouncer_exporter['enable'] = false gitlab_monitor['enable'] = false gitaly['enable'] = false - + redis['bind'] = '0.0.0.0' redis['port'] = '6379' redis['password'] = 'SECRET_PASSWORD_HERE' - + gitlab_rails['auto_migrate'] = false ``` @@ -74,7 +74,7 @@ Omnibus: 1. Note the Redis node's IP address or hostname, port, and Redis password. These will be necessary when configuring the GitLab application servers later. -1. [Enable Monitoring](#enable-monitoring) +1. [Enable Monitoring](#enable-monitoring) Advanced configuration options are supported and can be added if needed. @@ -91,10 +91,10 @@ environments including [Horizontal](README.md#horizontal), ### Provide your own Redis instance **[CORE ONLY]** -If you want to use your own deployed Redis instance(s), -see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only) -for more details. However, you can use the GitLab Omnibus package to easily -deploy the bundled Redis. +If you want to use your own deployed Redis instance(s), +see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only) +for more details. However, you can use the GitLab Omnibus package to easily +deploy the bundled Redis. ### High Availability with GitLab Omnibus **[PREMIUM ONLY]** @@ -368,7 +368,7 @@ The prerequisites for a HA Redis setup are the following: ```ruby # Specify server role as 'redis_master_role' roles ['redis_master_role'] - + # IP address pointing to a local IP that the other machines can reach to. # You can also set bind to '0.0.0.0' which listen in all interfaces. # If you really need to bind to an external accessible IP, make @@ -382,7 +382,7 @@ The prerequisites for a HA Redis setup are the following: # Set up password authentication for Redis (use the same password in all nodes). redis['password'] = 'redis-password-goes-here' ``` - + 1. Only the primary GitLab application server should handle migrations. To prevent database migrations from running on upgrade, add the following @@ -394,8 +394,8 @@ The prerequisites for a HA Redis setup are the following: 1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. -> Note: You can specify multiple roles like sentinel and redis as: -> roles ['redis_sentinel_role', 'redis_master_role']. Read more about high +> Note: You can specify multiple roles like sentinel and redis as: +> roles ['redis_sentinel_role', 'redis_master_role']. Read more about high > availability roles at https://docs.gitlab.com/omnibus/roles/ ### Step 2. Configuring the slave Redis instances @@ -412,7 +412,7 @@ The prerequisites for a HA Redis setup are the following: ```ruby # Specify server role as 'redis_slave_role' roles ['redis_slave_role'] - + # IP address pointing to a local IP that the other machines can reach to. # You can also set bind to '0.0.0.0' which listen in all interfaces. # If you really need to bind to an external accessible IP, make @@ -443,8 +443,8 @@ The prerequisites for a HA Redis setup are the following: 1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. 1. Go through the steps again for all the other slave nodes. -> Note: You can specify multiple roles like sentinel and redis as: -> roles ['redis_sentinel_role', 'redis_slave_role']. Read more about high +> Note: You can specify multiple roles like sentinel and redis as: +> roles ['redis_sentinel_role', 'redis_slave_role']. Read more about high > availability roles at https://docs.gitlab.com/omnibus/roles/ --- @@ -754,28 +754,30 @@ gitlab_rails['redis_sentinels'] = [ > [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/3786) in GitLab 12.0. - If you enable Monitoring, it must be enabled on **all** Redis servers. +If you enable Monitoring, it must be enabled on **all** Redis servers. + +1. Make sure to collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z` - 1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration: +1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration: - ```ruby - # Enable service discovery for Prometheus - consul['enable'] = true - consul['monitoring_service_discovery'] = true + ```ruby + # Enable service discovery for Prometheus + consul['enable'] = true + consul['monitoring_service_discovery'] = true - # Replace placeholders - # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z - # with the addresses of the Consul server nodes - consul['configuration'] = { - retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z), - } + # Replace placeholders + # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z + # with the addresses of the Consul server nodes + consul['configuration'] = { + retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z), + } - # Set the network addresses that the exporters will listen on - node_exporter['listen_address'] = '0.0.0.0:9100' - redis_exporter['listen_address'] = '0.0.0.0:9121' - ``` + # Set the network addresses that the exporters will listen on + node_exporter['listen_address'] = '0.0.0.0:9100' + redis_exporter['listen_address'] = '0.0.0.0:9121' + ``` - 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. +1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. ## Advanced configuration diff --git a/doc/administration/operations/extra_sidekiq_processes.md b/doc/administration/operations/extra_sidekiq_processes.md index 286b99aceb5..7297507f599 100644 --- a/doc/administration/operations/extra_sidekiq_processes.md +++ b/doc/administration/operations/extra_sidekiq_processes.md @@ -1,70 +1,132 @@ # Extra Sidekiq processes **[STARTER ONLY]** -GitLab Enterprise Edition allows one to start an extra set of Sidekiq processes +NOTE: **Note:** +The information in this page applies only to Omnibus GitLab. + +GitLab Starter allows one to start an extra set of Sidekiq processes besides the default one. These processes can be used to consume a dedicated set of queues. This can be used to ensure certain queues always have dedicated workers, no matter the number of jobs that need to be processed. -## Starting extra processes via Omnibus GitLab +## Available Sidekiq queues -To enable `sidekiq-cluster`, you must apply the `sidekiq_cluster['enable'] = true` -setting `/etc/gitlab/gitlab.rb`: +For a list of the existing Sidekiq queues, check the following files: -```ruby -sidekiq_cluster['enable'] = true -``` +- [Queues for both GitLab Community and Enterprise Editions](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/app/workers/all_queues.yml) +- [Queues for GitLab Enterprise Editions only](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/app/workers/all_queues.yml) -You will then specify how many additional processes to create via `sidekiq-cluster` -as well as which queues for them to handle. This is done via the -`sidekiq_cluster['queue_groups']` setting. This is an array whose items contain -which queues to process. Each item in the array will equate to one additional -sidekiq process. +Each entry in the above files represents a queue on which extra Sidekiq processes +can be started. -As an example, to make additional sidekiq processes that process the -`elastic_indexer` and `mailers` queues, you would apply the following: +## Starting extra processes -```ruby -sidekiq_cluster['queue_groups'] = [ - "elastic_indexer", - "mailers" -] -``` +To start extra Sidekiq processes, you must enable `sidekiq-cluster`: -To have an additional sidekiq process handle multiple queues, you simply put a -comma after the first queue name and then put the next queue name: +1. Edit `/etc/gitlab/gitlab.rb` and add: -```ruby -sidekiq_cluster['queue_groups'] = [ - "elastic_indexer,elastic_commit_indexer", - "mailers" -] -``` + ```ruby + sidekiq_cluster['enable'] = true + ``` -Keep in mind, all changes must be followed by reconfiguring your GitLab -application via `sudo gitlab-ctl reconfigure`. +1. You will then need to specify how many additional processes to create via `sidekiq-cluster` + and which queue they should handle via the `sidekiq_cluster['queue_groups']` + array setting. Each item in the array equates to one additional Sidekiq + process, and values in each item determine the queues it works on. -### Monitoring + For example, the following setting adds additional Sidekiq processes to two + queues, one to `elastic_indexer` and one to `mailers`: -Once the Sidekiq processes are added, you can visit the "Background Jobs" + ```ruby + sidekiq_cluster['queue_groups'] = [ + "elastic_indexer", + "mailers" + ] + ``` + + To have an additional Sidekiq process handle multiple queues, add multiple + queue names to its item delimited by commas. For example: + + ```ruby + sidekiq_cluster['queue_groups'] = [ + "elastic_indexer, elastic_commit_indexer", + "mailers" + ] + ``` + +1. Save the file and reconfigure GitLab for the changes to take effect: + + ```sh + sudo gitlab-ctl reconfigure + ``` + +Once the extra Sidekiq processes are added, you can visit the "Background Jobs" section under the admin area in GitLab (`/admin/background_jobs`). -![Extra sidekiq processes](img/sidekiq-cluster.png) +![Extra Sidekiq processes](img/sidekiq-cluster.png) -### All queues with exceptions +## Negating settings -To have the additional sidekiq processes work on every queue EXCEPT the ones +To have the additional Sidekiq processes work on every queue **except** the ones you list: +1. After you follow the steps for [starting extra processes](#starting-extra-processes), + edit `/etc/gitlab/gitlab.rb` and add: + + ```ruby + sidekiq_cluster['negate'] = true + ``` + +1. Save the file and reconfigure GitLab for the changes to take effect: + + ```sh + sudo gitlab-ctl reconfigure + ``` + +## Ignore all GitHub import queues + +When [importing from GitHub](../../user/project/import/github.md), Sidekiq might +use all of its resources to perform those operations. To set up a separate +`sidekiq-cluster` process to ignore all GitHub import-related queues: + 1. Edit `/etc/gitlab/gitlab.rb` and add: ```ruby + sidekiq_cluster['enable'] = true sidekiq_cluster['negate'] = true + sidekiq_cluster['queue_groups'] = [ + "github_import_advance_stage", + "github_importer:github_import_import_diff_note", + "github_importer:github_import_import_issue", + "github_importer:github_import_import_note", + "github_importer:github_import_import_lfs_object", + "github_importer:github_import_import_pull_request", + "github_importer:github_import_refresh_import_jid", + "github_importer:github_import_stage_finish_import", + "github_importer:github_import_stage_import_base_data", + "github_importer:github_import_stage_import_issues_and_diff_notes", + "github_importer:github_import_stage_import_notes", + "github_importer:github_import_stage_import_lfs_objects", + "github_importer:github_import_stage_import_pull_requests", + "github_importer:github_import_stage_import_repository" + ] ``` -1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. +1. Save the file and reconfigure GitLab for the changes to take effect: + ```sh + sudo gitlab-ctl reconfigure + ``` -### Limiting concurrency +## Number of threads + +Each process defined under `sidekiq_cluster` starts with a +number of threads that equals the number of queues, plus one spare thread. +For example, a process that handles the `process_commit` and `post_receive` +queues will use three threads in total. + +## Limiting concurrency + +To limit the concurrency of the Sidekiq processes: 1. Edit `/etc/gitlab/gitlab.rb` and add: @@ -72,11 +134,22 @@ you list: sidekiq_cluster['concurrency'] = 25 ``` -1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. +1. Save the file and reconfigure GitLab for the changes to take effect: -Keep in mind, this normally would not exceed the number of CPU cores available. + ```sh + sudo gitlab-ctl reconfigure + ``` -### Modifying the check interval +For each queue group, the concurrency factor will be set to `min(number of queues, N)`. +Setting the value to 0 will disable the limit. Keep in mind this normally would +not exceed the number of CPU cores available. + +Each thread requires a Redis connection, so adding threads may +increase Redis latency and potentially cause client timeouts. See the [Sidekiq +documentation about Redis](https://github.com/mperham/sidekiq/wiki/Using-Redis) +for more details. + +## Modifying the check interval To modify the check interval for the additional Sidekiq processes: @@ -90,9 +163,14 @@ To modify the check interval for the additional Sidekiq processes: This tells the additional processes how often to check for enqueued jobs. -## Starting extra processes via command line +## Troubleshooting using the CLI -Starting extra Sidekiq processes can be done using the command +CAUTION: **Warning:** +It's recommended to use `/etc/gitlab/gitlab.rb` to configure the Sidekiq processes. +If you experience a problem, you should contact GitLab support. Use the command +line at your own risk. + +For debugging purposes, you can start extra Sidekiq processes by using the command `/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster`. This command takes arguments using the following syntax: @@ -111,29 +189,29 @@ see the relevant section in the [Sidekiq style guide](../../development/sidekiq_style_guide.md#queue-namespaces). For example, say you want to start 2 extra processes: one to process the -"process_commit" queue, and one to process the "post_receive" queue. This can be +`process_commit` queue, and one to process the `post_receive` queue. This can be done as follows: ```bash /opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit post_receive ``` -If you instead want to start one process processing both queues you'd use the +If you instead want to start one process processing both queues, you'd use the following syntax: ```bash /opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive ``` -If you want to have one Sidekiq process process the "process_commit" and -"post_receive" queues, and one process to process the "gitlab_shell" queue, +If you want to have one Sidekiq process dealing with the `process_commit` and +`post_receive` queues, and one process to process the `gitlab_shell` queue, you'd use the following: ```bash /opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive gitlab_shell ``` -### Monitoring +### Monitoring the `sidekiq-cluster` command The `sidekiq-cluster` command will not terminate once it has started the desired amount of Sidekiq processes. Instead, the process will continue running and @@ -172,24 +250,24 @@ command and not the PID(s) of the started Sidekiq processes. The Rails environment can be set by passing the `--environment` flag to the `sidekiq-cluster` command, or by setting `RAILS_ENV` to a non-empty value. The -default value is "development". +default value can be found in `/opt/gitlab/etc/gitlab-rails/env/RAILS_ENV`. -### All queues with exceptions +### Using negation You're able to run all queues in `sidekiq_queues.yml` file on a single or multiple processes with exceptions using the `--negate` flag. For example, say you want to run a single process for all queues, -except "process_commit" and "post_receive". You can do so by executing: +except `process_commit` and `post_receive`: ```bash -sidekiq-cluster process_commit,post_receive --negate +/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive --negate ``` -For multiple processes of all queues (except "process_commit" and "post_receive"): +For multiple processes of all queues (except `process_commit` and `post_receive`): ```bash -sidekiq-cluster process_commit,post_receive process_commit,post_receive --negate +/opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive process_commit,post_receive --negate ``` ### Limiting concurrency @@ -201,18 +279,3 @@ the `-m N` option. For example, this would cap the maximum number of threads to ```bash /opt/gitlab/embedded/service/gitlab-rails/ee/bin/sidekiq-cluster process_commit,post_receive -m 1 ``` - -For each queue group, the concurrency factor will be set to min(number of -queues, N). Setting the value to 0 will disable the limit. - -Note that each thread requires a Redis connection, so adding threads may -increase Redis latency and potentially cause client timeouts. See the [Sidekiq -documentation about Redis](https://github.com/mperham/sidekiq/wiki/Using-Redis) -for more details. - -## Number of threads - -Each process started using `sidekiq-cluster` (whether it be via command line or -via the gitlab.rb file) starts with a number of threads that equals the number -of queues, plus one spare thread. For example, a process that handles the -"process_commit" and "post_receive" queues will use 3 threads in total. diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md index ddac81328b9..49aaac06b46 100644 --- a/doc/api/merge_request_approvals.md +++ b/doc/api/merge_request_approvals.md @@ -222,7 +222,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/approvals "id": 1, "state": "active", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon", - "web_url": "http://localhost:3000/u/root" + "web_url": "http://localhost:3000/root" } } ], @@ -314,7 +314,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid/approvers "id": 1, "state": "active", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon", - "web_url": "http://localhost:3000/u/root" + "web_url": "http://localhost:3000/root" } } ], @@ -387,7 +387,7 @@ does not match, the response code will be `409`. "id": 1, "state": "active", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon", - "web_url": "http://localhost:3000/u/root" + "web_url": "http://localhost:3000/root" } }, { @@ -397,7 +397,7 @@ does not match, the response code will be `409`. "id": 2, "state": "active", "avatar_url": "http://www.gravatar.com/avatar/cf7ad14b34162a76d593e3affca2adca?s=80\u0026d=identicon", - "web_url": "http://localhost:3000/u/ryley" + "web_url": "http://localhost:3000/ryley" } } ], diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md index c63ec53414c..a7b402c3fb0 100644 --- a/doc/development/chatops_on_gitlabcom.md +++ b/doc/development/chatops_on_gitlabcom.md @@ -1,12 +1,13 @@ # Chatops on GitLab.com -Chatops on GitLab.com allows GitLabbers to run various automation tasks on GitLab.com using Slack. +ChatOps on GitLab.com allows GitLab team members to run various automation tasks on GitLab.com using Slack. ## Requesting access -GitLabbers may need access to Chatops on GitLab.com for administration tasks such as: +GitLab team-members may need access to Chatops on GitLab.com for administration +tasks such as: -- Configuring feature flags on staging. +- Configuring feature flags. - Running `EXPLAIN` queries against the GitLab.com production replica. To request access to Chatops on GitLab.com: @@ -18,4 +19,4 @@ To request access to Chatops on GitLab.com: - [Chatops Usage](https://docs.gitlab.com/ee/ci/chatops/README.html) - [Understanding EXPLAIN plans](understanding_explain_plans.md) - - [Feature Groups](feature_flags.md#feature-groups) + - [Feature Groups](feature_flags/development.md#feature-groups) diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index ff6dc16d1a0..7cd3d82ec4e 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -100,6 +100,13 @@ use regular Markdown markup, following the rules in the linked style guide. Note that Kramdown-specific markup (e.g., `{:.class}`) will not render properly on GitLab instances under [`/help`](index.md#gitlab-help). +Hard-coded HTML is valid, although it's discouraged to be used while we have `/help`. HTML is permitted as long as: + +- There's no equivalent markup in markdown. +- Advanced tables are necessary. +- Special styling is required. +- Reviewed and approved by a technical writer. + ## Structure ### Organize by topic, not by type @@ -143,7 +150,8 @@ The table below shows what kind of documentation goes where. a proper naming would be `import_projects_from_github.md`. The same rule applies to images. 1. For image files, do not exceed 100KB. -1. We do not yet support embedded videos. Please link out. +1. Do not upload video files to the product repositories. +[Link or embed videos](#videos) instead. 1. There are four main directories, `user`, `administration`, `api` and `development`. 1. The `doc/user/` directory has five main subdirectories: `project/`, `group/`, `profile/`, `dashboard/` and `admin_area/`. @@ -207,6 +215,7 @@ Do not include the same information in multiple places. [Link to a SSOT instead. ## Text +- [Write in markdown](#markdown). - Splitting long lines (preferably up to 100 characters) can make it easier to provide feedback on small chunks of text. - Insert an empty line for new paragraphs. - Use sentence case for titles, headings, labels, menu items, and buttons. @@ -430,7 +439,7 @@ To indicate the steps of navigation through the UI: - Images should be used (only when necessary) to _illustrate_ the description of a process, not to _replace_ it. - Max image size: 100KB (gifs included). -- The GitLab docs do not support videos yet. +- See also how to link and embed [videos](#videos) to illustrate the docs. Inside the document: @@ -455,6 +464,85 @@ directly to an HTML `img` tag: <img src="path/to/image.jpg" alt="Alt text (required)" class="image-noshadow"> ``` +## Videos + +Adding GitLab's existing YouTube video tutorials to the documentation is +highly encouraged, unless the video is outdated. Videos should not +replace documentation, but complement or illustrate it. If content in a video is +fundamental to a feature and its key use cases, but this is not adequately covered in the documentation, +add this detail to the documentation text or create an issue to review the video and do so. + +Do not upload videos to the product repositories. [Link](#link-to-video) or [embed](#embed-videos) them instead. + +### Link to video + +To link out to a video, include a YouTube icon so that readers can +quickly and easily scan the page for videos before reading: + +```md +<i class="fa fa-youtube-play youtube" aria-hidden="true"></i> +For an overview, see [Video Title](link-to-video). +``` + +You can link any up-to-date video that is useful to the GitLab user. + +### Embed videos + +> [Introduced](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/472) in GitLab 12.1. + +GitLab docs (docs.gitlab.com) support embedded videos. + +You can only embed videos from +[GitLab's official YouTube account](https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg). +For videos from other sources, [link](#link-to-video) them instead. + +In most cases, it is better to [link to video](#link-to-video) instead, +because an embed takes up a lot of space on the page and can be distracting +to readers. + +To embed a video, follow the instructions below and make sure +you have your MR reviewed and approved by a technical writer. + +1. Copy the code below and paste it into your markdown file. + Leave a blank line above and below it. Do NOT edit the code + (don't remove or add any spaces, etc). +1. On YouTube, visit the video URL you want to display. Copy + the regular URL from your browser (`https://www.youtube.com/watch?v=VIDEO-ID`) + and replace the video title and link in the line under `<div class="video-fallback">`. +1. On YouTube, click **Share**, then **Embed**. +1. Copy the `<iframe>` source (`src`) **URL only** + (`https://www.youtube.com/embed/VIDEO-ID`), + and paste it, replacing the content of the `src` field in the + `iframe` tag. + +```html +leave a blank line here +<div class="video-fallback"> + See the video: [Video title](https://www.youtube.com/watch?v=MqL6BMOySIQ). +</div> +<figure class="video-container"> + <iframe src="https://www.youtube.com/embed/MqL6BMOySIQ" frameborder="0" allowfullscreen="true"> </iframe> +</figure> +leave a blank line here +``` + +This is how it renders on docs.gitlab.com: + +<div class="video-fallback"> + See the video: [What is GitLab](https://www.youtube.com/watch?v=enMumwvLAug). +</div> +<figure class="video-container"> + <iframe src="https://www.youtube.com/embed/MqL6BMOySIQ" frameborder="0" allowfullscreen="true"> </iframe> +</figure> + +> Notes: +> +> - The `figure` tag is required for semantic SEO and the `video_container` +class is necessary to make sure the video is responsive and displays +nicely on different mobile devices. +> - The `<div class="video-fallback">` is a fallback necessary for GitLab's +`/help`, as GitLab's markdown processor does not support iframes. It's hidden on the docs site but will be displayed on GitLab's `/help`. + ## Code blocks - Always wrap code added to a sentence in inline code blocks (``` ` ```). diff --git a/doc/development/fe_guide/accessibility.md b/doc/development/fe_guide/accessibility.md index df32242a522..64c793cfd64 100644 --- a/doc/development/fe_guide/accessibility.md +++ b/doc/development/fe_guide/accessibility.md @@ -5,8 +5,16 @@ [Chrome Accessibility Developer Tools][chrome-accessibility-developer-tools] are useful for testing for potential accessibility problems in GitLab. -Accessibility best-practices and more in-depth information is available on -[the Audit Rules page][audit-rules] for the Chrome Accessibility Developer Tools. +The [axe][axe-website] browser extension (available for [Firefox][axe-firefox-extension] and [Chrome][axe-chrome-extension]) is +also a handy tool for running audits and getting feedback on markup, CSS and even potentially problematic color usages. + +Accessibility best-practices and more in-depth information are available on +[the Audit Rules page][audit-rules] for the Chrome Accessibility Developer Tools. The "[awesome a11y][awesome-a11y]" list is also a +useful compilation of accessibility-related material. [chrome-accessibility-developer-tools]: https://github.com/GoogleChrome/accessibility-developer-tools [audit-rules]: https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules +[axe-website]: https://www.deque.com/axe/ +[axe-firefox-extension]: https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/ +[axe-chrome-extension]: https://chrome.google.com/webstore/detail/axe/lhdoppojpmngadmnindnejefpokejbdd +[awesome-a11y]: https://github.com/brunopulis/awesome-a11y diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md index 13f0c5cc33e..6bad91d6287 100644 --- a/doc/development/feature_flags.md +++ b/doc/development/feature_flags.md @@ -1,127 +1 @@ -# Manage feature flags - -Starting from GitLab 9.3 we support feature flags for features in GitLab via -[Flipper](https://github.com/jnunemaker/flipper/). You should use the `Feature` -class (defined in `lib/feature.rb`) in your code to get, set and list feature -flags. - -During runtime you can set the values for the gates via the -[features API](../api/features.md) (accessible to admins only). - -## Feature groups - -Starting from GitLab 9.4 we support feature groups via -[Flipper groups](https://github.com/jnunemaker/flipper/blob/v0.10.2/docs/Gates.md#2-group). - -Feature groups must be defined statically in `lib/feature.rb` (in the -`.register_feature_groups` method), but their implementation can obviously be -dynamic (querying the DB etc.). - -Once defined in `lib/feature.rb`, you will be able to activate a -feature for a given feature group via the [`feature_group` param of the features API](../api/features.md#set-or-create-a-feature) - -For GitLab.com, [team members have access to feature flags through Chatops](chatops_on_gitlabcom.md). Only -percentage gates are supported at this time. Setting a feature to be used 50% of -the time, you should execute `/chatops run feature set my_feature_flag 50`. - -## Feature flags for user applications - -This document only covers feature flags used in the development of GitLab -itself. Feature flags in deployed user applications can be found at -[Feature Flags](../user/project/operations/feature_flags.md) - -## Developing with feature flags - -In general, it's better to have a group- or user-based gate, and you should prefer -it over the use of percentage gates. This would make debugging easier, as you -filter for example logs and errors based on actors too. Furthermore, this allows -for enabling for the `gitlab-org` group first, while the rest of the users -aren't impacted. - -```ruby -# Good -Feature.enabled?(:feature_flag, project) - -# Avoid, if possible -Feature.enabled?(:feature_flag) -``` - -To use feature gates based on actors, the model needs to respond to -`flipper_id`. For example, to enable for the Foo model: - -```ruby -class Foo < ActiveRecord::Base - include FeatureGate -end -``` - -Features that are developed and are intended to be merged behind a feature flag -should not include a changelog entry. The entry should be added in the merge -request removing the feature flags. - -In the rare case that you need the feature flag to be on automatically, use -`default_enabled: true` when checking: - -```ruby -Feature.enabled?(:feature_flag, project, default_enabled: true) -``` - -For more information about rolling out changes using feature flags, refer to the -[Rolling out changes using feature flags](rolling_out_changes_using_feature_flags.md) -guide. - -### Frontend - -For frontend code you can use the method `push_frontend_feature_flag`, which is -available to all controllers that inherit from `ApplicationController`. Using -this method you can expose the state of a feature flag as follows: - -```ruby -before_action do - push_frontend_feature_flag(:vim_bindings) -end - -def index - # ... -end - -def edit - # ... -end -``` - -You can then check for the state of the feature flag in JavaScript as follows: - -```javascript -if ( gon.features.vimBindings ) { - // ... -} -``` - -The name of the feature flag in JavaScript will always be camelCased, meaning -that checking for `gon.features.vim_bindings` would not work. - -### Specs - -In the test environment `Feature.enabled?` is stubbed to always respond to `true`, -so we make sure behavior under feature flag doesn't go untested in some non-specific -contexts. - -Whenever a feature flag is present, make sure to test _both_ states of the -feature flag. - -See the -[testing guide](testing_guide/best_practices.md#feature-flags-in-tests) -for information and examples on how to stub feature flags in tests. - -## Enabling a feature flag (in development) - -In the rails console (`rails c`), enter the following command to enable your feature flag - -```ruby -Feature.enable(:feature_flag_name) -``` - -## Enabling a feature flag (in production) - -Check how to [roll out changes using feature flags](rolling_out_changes_using_feature_flags.md). +This document was moved to [another location](feature_flags/index.md). diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md new file mode 100644 index 00000000000..c67467b7c11 --- /dev/null +++ b/doc/development/feature_flags/controls.md @@ -0,0 +1,123 @@ +# Access for enabling a feature flag in production + +In order to be able to turn on/off features behind feature flags in any of the +GitLab Inc. provided environments such as staging and production, you need to +have access to the chatops bot. Chatops bot is currently running on the ops instance, +which is different from GitLab.com or dev.gitlab.org. + +Follow the Chatops document to [request access](https://docs.gitlab.com/ee/development/chatops_on_gitlabcom.html#requesting-access). + +Once you are added to the project test if your access propagated, +run: + +``` +/chatops run feature --help +``` + +## Rolling out changes + +When the changes are deployed to the environments it is time to start +rolling out the feature to our users. The exact procedure of rolling out a +change is unspecified, as this can vary from change to change. However, in +general we recommend rolling out changes incrementally, instead of enabling them +for everybody right away. We also recommend you to _not_ enable a feature +_before_ the code is being deployed. +This allows you to separate rolling out a feature from a deploy, making it +easier to measure the impact of both separately. + +GitLab's feature library (using +[Flipper](https://github.com/jnunemaker/flipper), and covered in the [Feature +Flags process](process.md) guide) supports rolling out changes to a percentage of +users. This in turn can be controlled using [GitLab chatops](../../ci/chatops/README.md). + +For an up to date list of feature flag commands please see [the source +code](https://gitlab.com/gitlab-com/chatops/blob/master/lib/chatops/commands/feature.rb). +Note that all the examples in that file must be preceded by +`/chatops run`. + +If you get an error "Whoops! This action is not allowed. This incident +will be reported." that means your Slack account is not allowed to +change feature flags or you do not [have access](#access-for-enabling-a-feature-flag-in-production). + +### Enabling feature for staging and dev.gitlab.org + +As a first step in a feature rollout, you should enable the feature on <https://staging.gitlab.com> +and <https://dev.gitlab.org>. + +For example, to enable a feature for 25% of all users, run the following in +Slack: + +``` +/chatops run feature set new_navigation_bar 25 --dev +/chatops run feature set new_navigation_bar 25 --staging +``` + +These two environments have different scopes. +`dev.gitlab.org` is a production CE environment that has internal GitLab Inc. +traffic and is used for some development and other related work. +`staging.gitlab.com` has a smaller subset of GitLab.com database and repositories +and does not have regular traffic. Staging is an EE instance and can give you +a (very) rough estimate of how your feature will look/behave on GitLab.com. +Both of these instances are connected to Sentry so make sure you check the projects +there for any exceptions while testing your feature after enabling the feature flag. + +Once you are confident enough that these environments are in a good state with your +feature enabled, you can roll out the change to GitLab.com. + +## Enabling feature for GitLab.com + +Similar to above, to enable a feature for 25% of all users, run the following in +Slack: + +``` +/chatops run feature set new_navigation_bar 25 +``` + +This will enable the feature for GitLab.com, with `new_navigation_bar` being the +name of the feature. + +If you are not certain what percentages to use, simply use the following steps: + +1. 25% +1. 50% +1. 75% +1. 100% + +Between every step you'll want to wait a little while and monitor the +appropriate graphs on <https://dashboards.gitlab.net>. The exact time to wait +may differ. For some features a few minutes is enough, while for others you may +want to wait several hours or even days. This is entirely up to you, just make +sure it is clearly communicated to your team, and the Production team if you +anticipate any potential problems. + +Feature gates can also be actor based, for example a feature could first be +enabled for only the `gitlab-ce` project. The project is passed by supplying a +`--project` flag: + +``` +/chatops run feature set --project=gitlab-org/gitlab-ce some_feature true +``` + +For groups the `--group` flag is available: + +``` +/chatops run feature set --group=gitlab-org some_feature true +``` + +## Cleaning up + +Once the change is deemed stable, submit a new merge request to remove the +feature flag. This ensures the change is available to all users and self-hosted +instances. Make sure to add the ~"feature flag" label to this merge request so +release managers are aware the changes are hidden behind a feature flag. If the +merge request has to be picked into a stable branch, make sure to also add the +appropriate "Pick into X" label (e.g. "Pick into XX.X"). +See [the process document](https://docs.gitlab.com/ee/development/feature_flags/process.html#including-a-feature-behind-feature-flag-in-the-final-release) for further details. + +When a feature gate has been removed from the code base, the value still exists +in the database. +This can be removed through ChatOps: + +``` +/chatops run feature delete some_feature +``` diff --git a/doc/development/feature_flags/development.md b/doc/development/feature_flags/development.md new file mode 100644 index 00000000000..238052529d9 --- /dev/null +++ b/doc/development/feature_flags/development.md @@ -0,0 +1,131 @@ +# Developing with feature flags + +In general, it's better to have a group- or user-based gate, and you should prefer +it over the use of percentage gates. This would make debugging easier, as you +filter for example logs and errors based on actors too. Furthermore, this allows +for enabling for the `gitlab-org` or `gitlab-com` group first, while the rest of +the users aren't impacted. + +```ruby +# Good +Feature.enabled?(:feature_flag, project) + +# Avoid, if possible +Feature.enabled?(:feature_flag) +``` + +To use feature gates based on actors, the model needs to respond to +`flipper_id`. For example, to enable for the Foo model: + +```ruby +class Foo < ActiveRecord::Base + include FeatureGate +end +``` + +Features that are developed and are intended to be merged behind a feature flag +should not include a changelog entry. The entry should be added in the merge +request removing the feature flags. + +In the rare case that you need the feature flag to be on automatically, use +`default_enabled: true` when checking: + +```ruby +Feature.enabled?(:feature_flag, project, default_enabled: true) +``` + +The [`Project#feature_available?`][project-fa], +[`Namespace#feature_available?`][namespace-fa] (EE), and +[`License.feature_available?`][license-fa] (EE) methods all implicitly check for +a feature flag by the same name as the provided argument. + +For example if a feature is license-gated, there's no need to add an additional +explicit feature flag check since the flag will be checked as part of the +`License.feature_available?` call. Similarly, there's no need to "clean up" a +feature flag once the feature has reached general availability. + +You'd still want to use an explicit `Feature.enabled?` check if your new feature +isn't gated by a License or Plan. + +[project-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/app/models/project_feature.rb#L63-68 +[namespace-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/ee/namespace.rb#L71-85 +[license-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/license.rb#L293-300 + +An important side-effect of the implicit feature flags mentioned above is that +unless the feature is explicitly disabled or limited to a percentage of users, +the feature flag check will default to `true`. + +As an example, if you were to ship the backend half of a feature behind a flag, +you'd want to explicitly disable that flag until the frontend half is also ready +to be shipped. [You can do this via Chatops](https://docs.gitlab.com/ee/development/feature_flags/controls.html): + +``` +/chatops run feature set some_feature 0 +``` + +Note that you can do this at any time, even before the merge request using the +flag has been merged! + +## Feature groups + +Starting from GitLab 9.4 we support feature groups via +[Flipper groups](https://github.com/jnunemaker/flipper/blob/v0.10.2/docs/Gates.md#2-group). + +Feature groups must be defined statically in `lib/feature.rb` (in the +`.register_feature_groups` method), but their implementation can obviously be +dynamic (querying the DB etc.). + +Once defined in `lib/feature.rb`, you will be able to activate a +feature for a given feature group via the [`feature_group` param of the features API](../../api/features.md#set-or-create-a-feature) + +### Frontend + +For frontend code you can use the method `push_frontend_feature_flag`, which is +available to all controllers that inherit from `ApplicationController`. Using +this method you can expose the state of a feature flag as follows: + +```ruby +before_action do + push_frontend_feature_flag(:vim_bindings) +end + +def index + # ... +end + +def edit + # ... +end +``` + +You can then check for the state of the feature flag in JavaScript as follows: + +```javascript +if ( gon.features.vimBindings ) { + // ... +} +``` + +The name of the feature flag in JavaScript will always be camelCased, meaning +that checking for `gon.features.vim_bindings` would not work. + +### Specs + +In the test environment `Feature.enabled?` is stubbed to always respond to `true`, +so we make sure behavior under feature flag doesn't go untested in some non-specific +contexts. + +Whenever a feature flag is present, make sure to test _both_ states of the +feature flag. + +See the +[testing guide](../testing_guide/best_practices.md#feature-flags-in-tests) +for information and examples on how to stub feature flags in tests. + +### Enabling a feature flag (in development) + +In the rails console (`rails c`), enter the following command to enable your feature flag + +```ruby +Feature.enable(:feature_flag_name) +``` diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md new file mode 100644 index 00000000000..56872f8c075 --- /dev/null +++ b/doc/development/feature_flags/index.md @@ -0,0 +1,12 @@ +# Feature flags in development of GitLab + +Feature flags can be used to gradually roll out changes, be +it a new feature, or a performance improvement. By using feature flags, we can +comfortably measure the impact of our changes, while still being able to easily +disable those changes, without having to revert an entire release. + +Before using feature flags for GitLab's development, read through the following: + +- [Process for using features flags](process.md). +- [Developing with feature flags documentation](development.md). +- [Controlling feature flags documentation](controls.md). diff --git a/doc/development/feature_flags/process.md b/doc/development/feature_flags/process.md new file mode 100644 index 00000000000..ee142b0da66 --- /dev/null +++ b/doc/development/feature_flags/process.md @@ -0,0 +1,130 @@ +# Feature flags process +## Feature flags for user applications + +This document only covers feature flags used in the development of GitLab +itself. Feature flags in deployed user applications can be found at +[Feature Flags feature documentation](../../user/project/operations/feature_flags.md). + +## Feature flags in GitLab development + +The following highlights should be considered when deciding if feature flags +should be leveraged: + +- By default, the feature flags should be **off**. +- Feature flags should remain in the codebase for as short period as possible +to reduce the need for feature flag accounting. +- The person operating with feature flags is responsible for clearly communicating +the status of a feature behind the feature flag with responsible stakeholders. +- Merge requests that make changes hidden behind a feature flag, or remove an +existing feature flag because a feature is deemed stable must have the +~"feature flag" label assigned. + +One might be tempted to think that feature flags will delay the release of a +feature by at least one month (= one release). This is not the case. A feature +flag does not have to stick around for a specific amount of time +(e.g. at least one release), instead they should stick around until the feature +is deemed stable. Stable means it works on GitLab.com without causing any +problems, such as outages. + +### When to use feature flags + +Starting with GitLab 11.4, developers are required to use feature flags for +non-trivial changes. Such changes include: + +- New features (e.g. a new merge request widget, epics, etc). +- Complex performance improvements that may require additional testing in + production, such as rewriting complex queries. +- Invasive changes to the user interface, such as a new navigation bar or the + removal of a sidebar. +- Adding support for importing projects from a third-party service. + +In all cases, those working on the changes can best decide if a feature flag is +necessary. For example, changing the color of a button doesn't need a feature +flag, while changing the navigation bar definitely needs one. In case you are +uncertain if a feature flag is necessary, simply ask about this in the merge +request, and those reviewing the changes will likely provide you with an answer. + +When using a feature flag for UI elements, make sure to _also_ use a feature +flag for the underlying backend code, if there is any. This ensures there is +absolutely no way to use the feature until it is enabled. + +### Including a feature behind feature flag in the final release + +In order to build a final release and present the feature for self-hosted +users, the feature flag should be at least defaulted to **on**. If the feature +is deemed stable and there is confidence that removing the feature flag is safe, +consider removing the feature flag altogether. Take into consideration that such +action can make the feature available on GitLab.com shortly after the change to +the feature flag is merged. + +Changing the default state or removing the feature flag has to be done before +the 22nd of the month, _at least_ 2 working days before, in order for the change +to be included in the final self-managed release. + +In addition to this, the feature behind feature flag should: + +- Run in all GitLab.com environments for a sufficient period of time. This time +period depends on the feature behind the feature flag, but as a general rule of +thumb 2-4 working days should be sufficient to gather enough feedback. +- The feature should be exposed to all users within the GitLab.com plan during +the above mentioned period of time. Exposing the feature to a smaller percentage +or only a group of users might not expose a sufficient amount of information to aid in +making a decision on feature stability. + +While rare, release managers may decide to reject picking or revert a change in +a stable branch, even when feature flags are used. This might be necessary if +the changes are deemed problematic, too invasive, or there simply isn't enough +time to properly measure how the changes behave on GitLab.com. + +### The cost of feature flags + +When reading the above, one might be tempted to think this procedure is going to +add a lot of work. Fortunately, this is not the case, and we'll show why. For +this example we'll specify the cost of the work to do as a number, ranging from +0 to infinity. The greater the number, the more expensive the work is. The cost +does _not_ translate to time, it's just a way of measuring complexity of one +change relative to another. + +Let's say we are building a new feature, and we have determined that the cost of +this is 10. We have also determined that the cost of adding a feature flag check +in a variety of places is 1. If we do not use feature flags, and our feature +works as intended, our total cost is 10. This however is the best case scenario. +Optimizing for the best case scenario is guaranteed to lead to trouble, whereas +optimizing for the worst case scenario is almost always better. + +To illustrate this, let's say our feature causes an outage, and there's no +immediate way to resolve it. This means we'd have to take the following steps to +resolve the outage: + +1. Revert the release. +1. Perform any cleanups that might be necessary, depending on the changes that + were made. +1. Revert the commit, ensuring the "master" branch remains stable. This is + especially necessary if solving the problem can take days or even weeks. +1. Pick the revert commit into the appropriate stable branches, ensuring we + don't block any future releases until the problem is resolved. + +As history has shown, these steps are time consuming, complex, often involve +many developers, and worst of all: our users will have a bad experience using +GitLab.com until the problem is resolved. + +Now let's say that all of this has an associated cost of 10. This means that in +the worst case scenario, which we should optimize for, our total cost is now 20. + +If we had used a feature flag, things would have been very different. We don't +need to revert a release, and because feature flags are disabled by default we +don't need to revert and pick any Git commits. In fact, all we have to do is +disable the feature, and in the worst case, perform cleanup. Let's say that +the cost of this is 2. In this case, our best case cost is 11: 10 to build the +feature, and 1 to add the feature flag. The worst case cost is now 13: 10 to +build the feature, 1 to add the feature flag, and 2 to disable and clean up. + +Here we can see that in the best case scenario the work necessary is only a tiny +bit more compared to not using a feature flag. Meanwhile, the process of +reverting our changes has been made significantly and reliably cheaper. + +In other words, feature flags do not slow down the development process. Instead, +they speed up the process as managing incidents now becomes _much_ easier. Once +continuous deployments are easier to perform, the time to iterate on a feature +is reduced even further, as you no longer need to wait weeks before your changes +are available on GitLab.com. diff --git a/doc/development/new_fe_guide/tips.md b/doc/development/new_fe_guide/tips.md index 4564f678ec0..879b54bd93c 100644 --- a/doc/development/new_fe_guide/tips.md +++ b/doc/development/new_fe_guide/tips.md @@ -10,16 +10,16 @@ yarn clean ## Creating feature flags in development -The process for creating a feature flag is the same as [enabling a feature flag in development](../feature_flags.md#enabling-a-feature-flag-in-development). +The process for creating a feature flag is the same as [enabling a feature flag in development](../feature_flags/development.md#enabling-a-feature-flag-in-development). Your feature flag can now be: -- [made available to the frontend](../feature_flags.md#frontend) via the `gon` -- queried in [tests](../feature_flags.md#specs) -- queried in HAML templates and ruby files via the `Feature.enabled?(:my_shiny_new_feature_flag)` method +- [Made available to the frontend](../feature_flags/development.md#frontend) via the `gon` +- Queried in [tests](../feature_flags/development.md#specs) +- Queried in HAML templates and ruby files via the `Feature.enabled?(:my_shiny_new_feature_flag)` method ### More on feature flags - [Deleting a feature flag](../../api/features.md#delete-a-feature) -- [Manage feature flags](../feature_flags.md) +- [Manage feature flags](../feature_flags/process.md) - [Feature flags API](../../api/features.md) diff --git a/doc/development/rolling_out_changes_using_feature_flags.md b/doc/development/rolling_out_changes_using_feature_flags.md index 84028b1b342..6bad91d6287 100644 --- a/doc/development/rolling_out_changes_using_feature_flags.md +++ b/doc/development/rolling_out_changes_using_feature_flags.md @@ -1,225 +1 @@ -# Rolling out changes using feature flags - -[Feature flags](feature_flags.md) can be used to gradually roll out changes, be -it a new feature, or a performance improvement. By using feature flags, we can -comfortably measure the impact of our changes, while still being able to easily -disable those changes, without having to revert an entire release. - -## When to use feature flags - -Starting with GitLab 11.4, developers are required to use feature flags for -non-trivial changes. Such changes include: - -- New features (e.g. a new merge request widget, epics, etc). -- Complex performance improvements that may require additional testing in - production, such as rewriting complex queries. -- Invasive changes to the user interface, such as a new navigation bar or the - removal of a sidebar. -- Adding support for importing projects from a third-party service. - -In all cases, those working on the changes can best decide if a feature flag is -necessary. For example, changing the color of a button doesn't need a feature -flag, while changing the navigation bar definitely needs one. In case you are -uncertain if a feature flag is necessary, simply ask about this in the merge -request, and those reviewing the changes will likely provide you with an answer. - -When using a feature flag for UI elements, make sure to _also_ use a feature -flag for the underlying backend code, if there is any. This ensures there is -absolutely no way to use the feature until it is enabled. - -## The cost of feature flags - -When reading the above, one might be tempted to think this procedure is going to -add a lot of work. Fortunately, this is not the case, and we'll show why. For -this example we'll specify the cost of the work to do as a number, ranging from -0 to infinity. The greater the number, the more expensive the work is. The cost -does _not_ translate to time, it's just a way of measuring complexity of one -change relative to another. - -Let's say we are building a new feature, and we have determined that the cost of -this is 10. We have also determined that the cost of adding a feature flag check -in a variety of places is 1. If we do not use feature flags, and our feature -works as intended, our total cost is 10. This however is the best case scenario. -Optimising for the best case scenario is guaranteed to lead to trouble, whereas -optimising for the worst case scenario is almost always better. - -To illustrate this, let's say our feature causes an outage, and there's no -immediate way to resolve it. This means we'd have to take the following steps to -resolve the outage: - -1. Revert the release. -1. Perform any cleanups that might be necessary, depending on the changes that - were made. -1. Revert the commit, ensuring the "master" branch remains stable. This is - especially necessary if solving the problem can take days or even weeks. -1. Pick the revert commit into the appropriate stable branches, ensuring we - don't block any future releases until the problem is resolved. - -As history has shown, these steps are time consuming, complex, often involve -many developers, and worst of all: our users will have a bad experience using -GitLab.com until the problem is resolved. - -Now let's say that all of this has an associated cost of 10. This means that in -the worst case scenario, which we should optimise for, our total cost is now 20. - -If we had used a feature flag, things would have been very different. We don't -need to revert a release, and because feature flags are disabled by default we -don't need to revert and pick any Git commits. In fact, all we have to do is -disable the feature, and in the worst case, perform cleanup. Let's say that -the cost of this is 2. In this case, our best case cost is 11: 10 to build the -feature, and 1 to add the feature flag. The worst case cost is now 13: 10 to -build the feature, 1 to add the feature flag, and 2 to disable and clean up. - -Here we can see that in the best case scenario the work necessary is only a tiny -bit more compared to not using a feature flag. Meanwhile, the process of -reverting our changes has been made significantly and reliably cheaper. - -In other words, feature flags do not slow down the development process. Instead, -they speed up the process as managing incidents now becomes _much_ easier. Once -continuous deployments are easier to perform, the time to iterate on a feature -is reduced even further, as you no longer need to wait weeks before your changes -are available on GitLab.com. - -## Rolling out changes - -The procedure of using feature flags is straightforward, and similar to not -using them. You add the necessary tests (make sure to test both the on and off -states of your feature flag(s)), make sure they all pass, have the code -reviewed, etc. You then submit your merge request, and add the ~"feature flag" -label. This label is used to signal to release managers that your changes are -hidden behind a feature flag and that it is safe to pick the MR into a stable -branch, without the need for an exception request. - -When the changes are deployed it is time to start rolling out the feature to our -users. The exact procedure of rolling out a change is unspecified, as this can -vary from change to change. However, in general we recommend rolling out changes -incrementally, instead of enabling them for everybody right away. We also -recommend you to _not_ enable a feature _before_ the code is being deployed. -This allows you to separate rolling out a feature from a deploy, making it -easier to measure the impact of both separately. - -GitLab's feature library (using -[Flipper](https://github.com/jnunemaker/flipper), and covered in the [Feature -Flags](feature_flags.md) guide) supports rolling out changes to a percentage of -users. This in turn can be controlled using [GitLab -chatops](../ci/chatops/README.md). - -For an up to date list of feature flag commands please see [the source -code](https://gitlab.com/gitlab-com/chatops/blob/master/lib/chatops/commands/feature.rb). -Note that all the examples in that file must be preceded by -`/chatops run`. - -If you get an error "Whoops! This action is not allowed. This incident -will be reported." that means your Slack account is not allowed to -change feature flags. To test if you are allowed to do anything at all, -run: - -``` -/chatops run feature --help -``` - -For example, to enable a feature for 25% of all users, run the following in -Slack: - -``` -/chatops run feature set new_navigation_bar 25 -``` - -This will enable the feature for GitLab.com, with `new_navigation_bar` being the -name of the feature. We can also enable the feature for <https://dev.gitlab.org> -or <https://staging.gitlab.com>: - -``` -/chatops run feature set new_navigation_bar 25 --dev -/chatops run feature set new_navigation_bar 25 --staging -``` - -If you are not certain what percentages to use, simply use the following steps: - -1. 25% -1. 50% -1. 75% -1. 100% - -Between every step you'll want to wait a little while and monitor the -appropriate graphs on <https://dashboards.gitlab.net>. The exact time to wait -may differ. For some features a few minutes is enough, while for others you may -want to wait several hours or even days. This is entirely up to you, just make -sure it is clearly communicated to your team, and the Production team if you -anticipate any potential problems. - -Feature gates can also be actor based, for example a feature could first be -enabled for only the `gitlab-ce` project. The project is passed by supplying a -`--project` flag: - -``` -/chatops run feature set --project=gitlab-org/gitlab-ce some_feature true -``` - -For groups the `--group` flag is available: - -``` -/chatops run feature set --group=gitlab-org some_feature true -``` - -Once a change is deemed stable, submit a new merge request to remove the -feature flag. This ensures the change is available to all users and self-hosted -instances. Make sure to add the ~"feature flag" label to this merge request so -release managers are aware the changes are hidden behind a feature flag. If the -merge request has to be picked into a stable branch (e.g. after the 7th), make -sure to also add the appropriate "Pick into X" label (e.g. "Pick into 11.4"). - -One might be tempted to think this will delay the release of a feature by at -least one month (= one release). This is not the case. A feature flag does not -have to stick around for a specific amount of time (e.g. at least one release), -instead they should stick around until the feature is deemed stable. Stable -means it works on GitLab.com without causing any problems, such as outages. In -most cases this will translate to a feature (with a feature flag) being shipped -in RC1, followed by the feature flag being removed in RC2. This in turn means -the feature will be stable by the time we publish a stable package around the -22nd of the month. - -## Implicit feature flags - -The [`Project#feature_available?`][project-fa], -[`Namespace#feature_available?`][namespace-fa] (EE), and -[`License.feature_available?`][license-fa] (EE) methods all implicitly check for -a feature flag by the same name as the provided argument. - -For example if a feature is license-gated, there's no need to add an additional -explicit feature flag check since the flag will be checked as part of the -`License.feature_available?` call. Similarly, there's no need to "clean up" a -feature flag once the feature has reached general availability. - -You'd still want to use an explicit `Feature.enabled?` check if your new feature -isn't gated by a License or Plan. - -[project-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/app/models/project_feature.rb#L63-68 -[namespace-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/ee/namespace.rb#L71-85 -[license-fa]: https://gitlab.com/gitlab-org/gitlab-ee/blob/4cc1c62918aa4c31750cb21dfb1a6c3492d71080/ee/app/models/license.rb#L293-300 - -### Undefined feature flags default to "on" - -An important side-effect of the [implicit feature flags](#implicit-feature-flags) -mentioned above is that unless the feature is explicitly disabled or limited to a -percentage of users, the feature flag check will default to `true`. - -As an example, if you were to ship the backend half of a feature behind a flag, -you'd want to explicitly disable that flag until the frontend half is also ready -to be shipped. You can do this via ChatOps: - -``` -/chatops run feature set some_feature 0 -``` - -Note that you can do this at any time, even before the merge request using the -flag has been merged! - -### Cleaning up - -When a feature gate has been removed from the code base, the value still exists -in the database. This can be removed through ChatOps: - -``` -/chatops run feature delete some_feature -``` +This document was moved to [another location](feature_flags/index.md). diff --git a/doc/development/testing_guide/end_to_end/quick_start_guide.md b/doc/development/testing_guide/end_to_end/quick_start_guide.md index f96c85be1ba..041bdf716b3 100644 --- a/doc/development/testing_guide/end_to_end/quick_start_guide.md +++ b/doc/development/testing_guide/end_to_end/quick_start_guide.md @@ -242,7 +242,7 @@ module QA issue = Resource::Issue.fabricate_via_api! do |issue| issue.title = 'Issue to test the scoped labels' - issue.labels = @initial_label + issue.labels = [@initial_label] end [@new_label_same_scope, @new_label_different_scope].each do |label| @@ -365,6 +365,14 @@ Add the following `attribute :id` and `attribute :labels` right above the [`attr > We add the attributes above the existing attribute to keep them alphabetically organized. +Then, let's initialize an instance variable for labels to allow an empty array as default value when such information is not passed during the resource fabrication, since this optional. [Between the attributes and the `fabricate!` method](https://gitlab.com/gitlab-org/gitlab-ee/blob/1a1f1408728f19b2aa15887cd20bddab7e70c8bd/qa/qa/resource/issue.rb#L18), add the following: + +```ruby +def initialize + @labels = [] +end +``` + Next, add the following code right below the [`fabricate!`](https://gitlab.com/gitlab-org/gitlab-ee/blob/d3584e80b4236acdf393d815d604801573af72cc/qa/qa/resource/issue.rb#L27) method. ```ruby @@ -378,7 +386,7 @@ end def api_post_body { - labels: [labels], + labels: labels, title: title } end diff --git a/doc/gitlab-basics/command-line-commands.md b/doc/gitlab-basics/command-line-commands.md index 1cf883679d7..b7e6844f43a 100644 --- a/doc/gitlab-basics/command-line-commands.md +++ b/doc/gitlab-basics/command-line-commands.md @@ -29,7 +29,9 @@ git clone PASTE HTTPS OR SSH HERE A clone of the project will be created in your computer. ->**Note:** If you clone your project via a URL that contains special characters, make sure that characters are URL-encoded. +NOTE: **Note:** +If you clone your project via a URL that contains special characters, make sure +that characters are URL-encoded. ### Go into a project directory to work in it @@ -86,12 +88,18 @@ cat README.md ### Remove a file +DANGER: **Danger:** +This will permanently delete the file. + ``` rm NAME-OF-FILE ``` ### Remove a directory and all of its contents +DANGER: **Danger:** +This will permanently delete the directory and all of its contents. + ``` rm -r NAME-OF-DIRECTORY ``` @@ -113,9 +121,13 @@ history You will be asked for an administrator’s password. ``` -sudo +sudo COMMAND ``` +CAUTION: **Caution:** +Be careful of the commands you run with `sudo`. Certain commands may cause +damage to your data and system. + ### Show which directory I am in ``` diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 92122fca7dd..68c1bcbc801 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -9,7 +9,7 @@ as the hardware requirements that are needed to install and use GitLab. ## Operating Systems -### Supported Unix distributions +### Supported Linux distributions - Ubuntu - Debian @@ -21,7 +21,7 @@ as the hardware requirements that are needed to install and use GitLab. For the installations options, see [the main installation page](README.md). -### Unsupported Unix distributions +### Unsupported Linux distributions and Unix-like operating systems - Arch Linux - Fedora @@ -29,13 +29,13 @@ For the installations options, see [the main installation page](README.md). - Gentoo - macOS -On the above unsupported distributions is still possible to install GitLab yourself. +Installation of GitLab on these operating systems is possible, but not supported. Please see the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/installation/) for more information. -### Non-Unix operating systems such as Windows +### Microsoft Windows -GitLab is developed for Unix operating systems. -It does **not** run on Windows, and we have no plans to support it in the near future. For the latest development status view this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46567). +GitLab is developed for Linux-based operating systems. +It does **not** run on Microsoft Windows, and we have no plans to support it in the near future. For the latest development status view this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46567). Please consider using a virtual machine to run GitLab. ## Ruby versions diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 702245b22a0..41d12128e51 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -723,7 +723,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac | `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | From Gitlab 11.11, this variable can be used to set a username to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD) | | `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From Gitlab 11.11, this variable can be used to set a password to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_USERNAME) | | `REPLICAS` | The number of replicas to deploy; defaults to 1. | -| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. This takes precedence over `REPLICAS`; defaults to 1. | +| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. | | `CANARY_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md); defaults to 1 | | `CANARY_PRODUCTION_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. This takes precedence over `CANARY_REPLICAS`; defaults to 1 | | `ADDITIONAL_HOSTS` | Fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. | diff --git a/doc/university/process/README.md b/doc/university/process/README.md index fdf6224f7f6..b278e02ccd5 100644 --- a/doc/university/process/README.md +++ b/doc/university/process/README.md @@ -9,11 +9,11 @@ title: University | Process # Suggesting improvements If you would like to teach a class or participate or help in any way please -submit a merge request and assign it to [Job](https://gitlab.com/u/JobV). +submit a merge request and assign it to [Job](https://gitlab.com/JobV). If you have suggestions for additional courses you would like to see, please submit a merge request to add an upcoming class, assign to -[Chad](https://gitlab.com/u/chadmalchow) and /cc [Job](https://gitlab.com/u/JobV). +[Chad](https://gitlab.com/chadmalchow) and /cc [Job](https://gitlab.com/JobV). ## Adding classes @@ -31,4 +31,4 @@ please submit a merge request to add an upcoming class, assign to 1. Please upload any video recordings to our Youtube channel. We prefer them to be public, if needed they can be unlisted but if so they should be linked from this page. -1. Please create a merge request and assign to [Erica](https://gitlab.com/u/Erica). +1. Please create a merge request and assign to [Erica](https://gitlab.com/Erica). diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md index 26d764fa2cf..8d4ffd93f59 100644 --- a/doc/user/group/clusters/index.md +++ b/doc/user/group/clusters/index.md @@ -138,14 +138,6 @@ The result will then be: - The Staging cluster will be used for the `deploy to staging` job. - The Production cluster will be used for the `deploy to production` job. -## Unavailable features - -The following features are not currently available for group-level clusters: - -1. Terminals (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55487)). -1. Pod logs (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55488)). -1. Deployment boards (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55489)). - <!-- ## Troubleshooting Include any troubleshooting steps that you can foresee. If you know beforehand what issues diff --git a/doc/user/img/color_inline_colorchip_render_gfm.png b/doc/user/img/color_inline_colorchip_render_gfm.png Binary files differdeleted file mode 100644 index 6f93dbeeb10..00000000000 --- a/doc/user/img/color_inline_colorchip_render_gfm.png +++ /dev/null diff --git a/doc/user/img/markdown_inline_diffs_tags_rendered.png b/doc/user/img/markdown_inline_diffs_tags_rendered.png Binary files differdeleted file mode 100644 index 4279a20b5a0..00000000000 --- a/doc/user/img/markdown_inline_diffs_tags_rendered.png +++ /dev/null diff --git a/doc/user/img/math_inline_sup_render_gfm.png b/doc/user/img/math_inline_sup_render_gfm.png Binary files differdeleted file mode 100644 index 3ee2abb14df..00000000000 --- a/doc/user/img/math_inline_sup_render_gfm.png +++ /dev/null diff --git a/doc/user/img/task_list_ordered_render_gfm.png b/doc/user/img/task_list_ordered_render_gfm.png Binary files differdeleted file mode 100644 index 98ec791e958..00000000000 --- a/doc/user/img/task_list_ordered_render_gfm.png +++ /dev/null diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 31c8093ced7..16df6d93277 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -1,15 +1,20 @@ # GitLab Markdown -This markdown guide is **valid for GitLab's system markdown entries and files**. -It is not valid for the [GitLab documentation website](https://docs.gitlab.com) -nor [GitLab's main website](https://about.gitlab.com), as they both use -[Kramdown](https://kramdown.gettalong.org) as their markdown engine. -The documentation website uses an extended Kramdown gem, [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown). -Consult the [GitLab Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/) for a complete Kramdown reference. +This markdown guide is **valid only for GitLab's internal markdown rendering system for entries and files**. +It is **not** valid for the [GitLab documentation website](https://docs.gitlab.com) +or [GitLab's main website](https://about.gitlab.com), as they both use +[Kramdown](https://kramdown.gettalong.org) as their markdown engine. The documentation +website uses an extended Kramdown gem, [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown). +Consult the [GitLab Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/) +for a complete Kramdown reference. + +NOTE: **Note:** We encourage you to view this document as [rendered by GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md). ## GitLab Flavored Markdown (GFM) -GitLab uses "GitLab Flavored Markdown" (GFM). It extends the [CommonMark specification][commonmark-spec] (which is based on standard Markdown) in a few significant ways to add additional useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/). +GitLab uses "GitLab Flavored Markdown" (GFM). It extends the [CommonMark specification](https://spec.commonmark.org/current/) +(which is based on standard Markdown) in several ways to add additional useful functionality. +It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/). You can use GFM in the following areas: @@ -22,35 +27,29 @@ You can use GFM in the following areas: - Markdown documents inside repositories - Epics **[ULTIMATE]** -You can also use other rich text files in GitLab. You might have to install a -dependency to do so. Please see the [`github-markup` gem readme](https://github.com/gitlabhq/markup#markups) for more information. - -> **Notes:** -> -> We encourage you to view this document as [rendered by GitLab itself](markdown.md). -> -> As of 11.1, GitLab uses the [CommonMark Ruby Library][commonmarker] for Markdown -processing of all new issues, merge requests, comments, and other Markdown content -in the GitLab system. As of 11.3, wiki pages and Markdown files (`.md`) in the -repositories are also processed with CommonMark. As of 11.8, the [Redcarpet -Ruby library][redcarpet] has been removed and all issues/comments, including -those from pre-11.1, are now processed using [CommonMark Ruby -Library][commonmarker]. -> -> The documentation website had its [markdown engine migrated from Redcarpet to Kramdown](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/108) -in October 2018. -> -> _Where there are significant differences, we will try to call them out in this document._ +You can also use other rich text files in GitLab. You might have to install a dependency +to do so. Please see the [`gitlab-markup` gem project](https://gitlab.com/gitlab-org/gitlab-markup) +for more information. + +### Transition from Redcarpet to CommonMark -### Transitioning to CommonMark +Since 11.1, GitLab uses the [CommonMark Ruby Library](https://github.com/gjtorikian/commonmarker) +for Markdown processing of all new issues, merge requests, comments, and other Markdown +content in the GitLab system. Since 11.3, wiki pages and Markdown files (`*.md`) in +repositories are also processed with CommonMark. As of 11.8, the [Redcarpet Ruby library](https://github.com/vmg/redcarpet) +has been removed and all issues and comments, including those from pre-11.1, are now processed +using the [CommonMark Ruby Library](https://github.com/gjtorikian/commonmarker). -You may have older issues/merge requests or Markdown documents in your -repository that were written using some of the nuances of RedCarpet's version +The documentation website had its [markdown engine migrated from Redcarpet to Kramdown](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/108) +in October 2018. + +You may have older issues, merge requests, or Markdown documents in your +repository that were written using some of the nuances of GitLab's RedCarpet version of Markdown. Since CommonMark uses a slightly stricter syntax, these documents -may now display a little strangely since we've transitioned to CommonMark. -Numbered lists with nested lists in particular can be displayed incorrectly. +may now display a little differently since we've transitioned to CommonMark. -It is usually quite easy to fix. In the case of a nested list such as this: +It is usually quite easy to fix. For example, numbered lists with nested lists may +render incorrectly: ```markdown 1. Chocolate @@ -58,7 +57,14 @@ It is usually quite easy to fix. In the case of a nested list such as this: - milk ``` -simply add a space to each nested item: +1. Chocolate + - dark + - milk + +--- + +Simply add a space to each nested item to align the `-` with the first character of +the top list item (`C` in this case): ```markdown 1. Chocolate @@ -66,515 +72,674 @@ simply add a space to each nested item: - milk ``` -In the documentation below, we try to highlight some of the differences. +1. Chocolate + - dark + - milk + +NOTE: **Note:** We will flag any significant differences between Redcarpet and CommonMark + markdown in this document. If you have a large volume of Markdown files, it can be tedious to determine -if they will be displayed correctly or not. You can use the +if they will display correctly or not. You can use the [diff_redcarpet_cmark](https://gitlab.com/digitalmoksha/diff_redcarpet_cmark) -tool (not an officially supported product) to generate a list of files and +tool (not an officially supported product) to generate a list of files, and the differences between how RedCarpet and CommonMark render the files. It can give -you a great idea if anything needs to be changed - many times nothing will need -to changed. +an indication if anything needs to be changed - often nothing will need +to change. + +### GFM extends standard markdown + +GitLab makes full use of the standard (CommonMark) formatting, but also includes additional +functionality useful for GitLab users. + +It makes use of [new markdown features](#new-GFM-markdown-extensions), +not found in standard markdown: + +- [Color "chips" written in HEX, RGB or HSL](#colors) +- [Diagrams and flowcharts using Mermaid](#diagrams-and-flowcharts-using-mermaid) +- [Emoji](#emoji) +- [Front matter](#front-matter) +- [Inline diffs](#inline-diff) +- [Math equations and symbols written in LaTeX](#math) +- [Special GitLab references](#special-gitlab-references) +- [Task Lists](#task-lists) +- [Wiki specific markdown](#wiki-specific-markdown) + +It also has [extended markdown features](#standard-markdown-and-extensions-in-gitlab), without +changing how standard markdown is used: + +| Standard markdown | Extended markdown in GitLab | +| ------------------------------------- | ------------------------- | +| [blockquotes](#blockquotes) | [multiline blockquotes](#multiline-blockquote) | +| [code blocks](#code-spans-and-blocks) | [colored code and syntax highlighting](#colored-code-and-syntax-highlighting) | +| [emphasis](#emphasis) | [multiple underscores in words](#multiple-underscores-in-words-and-mid-word-emphasis) +| [headers](#headers) | [linkable Header IDs](#header-ids-and-links) | +| [images](#images) | [embedded videos](#videos) | +| [linebreaks](#line-breaks) | [more linebreak control](#newlines) | +| [links](#links) | [automatically linking URLs](#url-auto-linking) | + +## New GFM markdown extensions + +### Colors -### Newlines +> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#colors). -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#newlines +It is possible to have color written in HEX, RGB or HSL format rendered with a color +indicator. -GFM honors the markdown specification in how [paragraphs and line breaks are handled][commonmark-spec]. +Supported formats (named colors are not supported): -A paragraph is simply one or more consecutive lines of text, separated by one or -more blank lines. -Line-breaks, or soft returns, are rendered if you end a line with two or more spaces: +- HEX: `` `#RGB[A]` `` or `` `#RRGGBB[AA]` `` +- RGB: `` `RGB[A](R, G, B[, A])` `` +- HSL: `` `HSL[A](H, S, L[, A])` `` -<!-- (Do *NOT* remove the two ending whitespaces in the following line.) --> -<!-- (They are needed for the Markdown text to render correctly.) --> - Roses are red [followed by two or more spaces] - Violets are blue +Color written inside backticks will be followed by a color "chip": - Sugar is sweet +```markdown +`#F00` +`#F00A` +`#FF0000` +`#FF0000AA` +`RGB(0,255,0)` +`RGB(0%,100%,0%)` +`RGBA(0,255,0,0.3)` +`HSL(540,70%,50%)` +`HSLA(540,70%,50%,0.3)` +``` -<!-- (Do *NOT* remove the two ending whitespaces in the following line.) --> -<!-- (They are needed for the Markdown text to render correctly.) --> -Roses are red -Violets are blue +`#F00` +`#F00A` +`#FF0000` +`#FF0000AA` +`RGB(0,255,0)` +`RGB(0%,100%,0%)` +`RGBA(0,255,0,0.3)` +`HSL(540,70%,50%)` +`HSLA(540,70%,50%,0.3)` -Sugar is sweet +### Diagrams and flowcharts using Mermaid -### Multiple underscores in words +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107) in +GitLab 10.3. -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiple-underscores-in-words +It is possible to generate diagrams and flowcharts from text using [Mermaid](https://mermaidjs.github.io/). +Visit the official page for more details. -It is not reasonable to italicize just _part_ of a word, especially when you're -dealing with code and names that often appear with multiple underscores. -Therefore, GFM ignores multiple underscores in words: +In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block: - perform_complicated_task +~~~ +```mermaid +graph TD; + A-->B; + A-->C; + B-->D; + C-->D; +``` +~~~ - do_this_and_do_that_and_another_thing +```mermaid +graph TD; + A-->B; + A-->C; + B-->D; + C-->D; +``` -perform_complicated_task +### Emoji -do_this_and_do_that_and_another_thing +> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji). -### URL auto-linking +```md +Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you: -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#url-auto-linking +:zap: You can use emoji anywhere GFM is supported. :v: -GFM will autolink almost any URL you copy and paste into your text: +You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that. - * https://www.google.com - * https://google.com/ - * ftp://ftp.us.debian.org/debian/ - * smb://foo/bar/baz - * irc://irc.freenode.net/gitlab - * http://localhost:3000 +If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes. -* https://www.google.com -* https://google.com/ -* ftp://ftp.us.debian.org/debian/ -* <a href="smb://foo/bar/baz">smb://foo/bar/baz</a> -* <a href="irc://irc.freenode.net/gitlab">irc://irc.freenode.net/gitlab</a> -* http://localhost:3000 +Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup: +``` -### Multiline blockquote +Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/monkey.png" width="20px" height="20px" style="display:inline;margin:0"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/star2.png" width="20px" height="20px" style="display:inline;margin:0"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speech_balloon.png" width="20px" height="20px" style="display:inline;margin:0">. Well we have a gift for you: -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiline-blockquote +<img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/zap.png" width="20px" height="20px" style="display:inline;margin:0">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/v.png" width="20px" height="20px" style="display:inline;margin:0"> -On top of standard Markdown [blockquotes](#blockquotes), which require prepending `>` to quoted lines, -GFM supports multiline blockquotes fenced by <code>>>></code>: +You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px" style="display:inline;margin:0"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px" style="display:inline;margin:0"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px" style="display:inline;margin:0"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px" style="display:inline;margin:0">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px" style="display:inline;margin:0"> you for that. -``` ->>> -If you paste a message from somewhere else +If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px" style="display:inline;margin:0">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px" style="display:inline;margin:0">. All you need to do is to look up one of the supported codes. -that +Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px" style="display:inline;margin:0"> -spans +> **Note:** The emoji example above uses hard-coded images for this documentation. The emoji, +when rendered within GitLab, may appear different depending on the OS and browser used. -multiple lines, +Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. -you can quote that without having to manually prepend `>` to every line! ->>> -``` +NOTE: **Note:** On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) +to get full native emoji support. Ubuntu 18.04 (like many modern Linux distros) has +this font installed by default. -<blockquote dir="auto"> -<p>If you paste a message from somewhere else</p> -<p>that</p> -<p>spans</p> -<p>multiple lines,</p> -<p>you can quote that without having to manually prepend <code>></code> to every line!</p> -</blockquote> +### Front matter -### Code and syntax highlighting +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23331) + in GitLab 11.6. -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting +Front matter is metadata included at the beginning of a markdown document, preceding +its content. This data can be used by static site generators such as [Jekyll](https://jekyllrb.com/docs/front-matter/), +[Hugo](https://gohugo.io/content-management/front-matter/), and many other applications. -_GitLab uses the [Rouge Ruby library][rouge] for syntax highlighting. For a -list of supported languages visit the Rouge website._ +When you view a Markdown file rendered by GitLab, any front matter is displayed as-is, +in a box at the top of the document, before the rendered HTML content. To view an example, +you can toggle between the source and rendered version of a [GitLab documentation file](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/README.md). -Blocks of code are either fenced by lines with three back-ticks <code>```</code>, -or are indented with four spaces. Only the fenced code blocks support syntax -highlighting: +In GitLab, front matter is only used in Markdown files and wiki pages, not the other +places where Markdown formatting is supported. It must be at the very top of the document, +and must be between delimiters, as explained below. -``` -Inline `code` has `back-ticks around` it. -``` +The following delimeters are supported: -Inline `code` has `back-ticks around` it. +- YAML (`---`): -Example: + ~~~yaml + --- + title: About Front Matter + example: + language: yaml + --- + ~~~ - ```javascript - var s = "JavaScript syntax highlighting"; - alert(s); - ``` +- TOML (`+++`): - ```python - def function(): - #indenting works just fine in the fenced code block - s = "Python syntax highlighting" - print s - ``` + ~~~toml + +++ + title = "About Front Matter" + [example] + language = "toml" + +++ + ~~~ - ```ruby - require 'redcarpet' - markdown = Redcarpet.new("Hello World!") - puts markdown.to_html - ``` +- JSON (`;;;`): - ``` - No language indicated, so no syntax highlighting. - s = "There is no highlighting for this." - But let's throw in a <b>tag</b>. - ``` + ~~~json + ;;; + { + "title": "About Front Matter" + "example": { + "language": "json" + } + } + ;;; + ~~~ -becomes: +Other languages are supported by adding a specifier to any of the existing +delimiters. For example: -```javascript -var s = "JavaScript syntax highlighting"; -alert(s); +```php +---php +$title = "About Front Matter"; +$example = array( + 'language' => "php", +); +--- ``` -```python -def function(): - #indenting works just fine in the fenced code block - s = "Python syntax highlighting" - print s -``` +### Inline diff -```ruby -require 'redcarpet' -markdown = Redcarpet.new("Hello World!") -puts markdown.to_html -``` +> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-diff). -``` -No language indicated, so no syntax highlighting. -s = "There is no highlighting for this." -But let's throw in a <b>tag</b>. -``` +With inline diff tags you can display {+ additions +} or [- deletions -]. -### Inline diff +The wrapping tags can be either curly braces or square brackets: -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-diff +```markdown +- {+ addition 1 +} +- [+ addition 2 +] +- {- deletion 3 -} +- [- deletion 4 -] +``` -With inline diffs tags you can display {+ additions +} or [- deletions -]. +- {+ addition 1 +} +- [+ addition 2 +] +- {- deletion 3 -} +- [- deletion 4 -] -The wrapping tags can be either curly braces or square brackets. +--- -Examples: +However the wrapping tags cannot be mixed: +```markdown +- {+ addition +] +- [+ addition +} +- {- deletion -] +- [- deletion -} ``` -- {+ additions +} -- [+ additions +] -- {- deletions -} -- [- deletions -] -``` -becomes: +### Math + +> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#math). + +It is possible to have math written with LaTeX syntax rendered using [KaTeX](https://github.com/Khan/KaTeX). -![inline diffs tags rendered](img/markdown_inline_diffs_tags_rendered.png) +Math written between dollar signs `$` will be rendered inline with the text. Math written +inside a [code block](#code-spans-and-blocks) with the language declared as `math`, will be rendered +on a separate line: -However the wrapping tags cannot be mixed as such: +~~~ +This math is inline $`a^2+b^2=c^2`$. +This is on a separate line + +```math +a^2+b^2=c^2 ``` -- {+ additions +] -- [+ additions +} -- {- deletions -] -- [- deletions -} +~~~ + +This math is inline $`a^2+b^2=c^2`$. + +This is on a separate line + +```math +a^2+b^2=c^2 ``` -### Emoji +_Be advised that KaTeX only supports a [subset](https://katex.org/docs/supported.html) of LaTeX._ -```md -Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you: +NOTE: **Note:** This also works for the asciidoctor `:stem: latexmath`. For details see +the [asciidoctor user manual](http://asciidoctor.org/docs/user-manual/#activating-stem-support). -:zap: You can use emoji anywhere GFM is supported. :v: +### Special GitLab references -You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that. +GFM recognizes special GitLab related references. For example, you can easily reference +an issue, a commit, a team member or even the whole team within a project. GFM will turn +that reference into a link so you can navigate between them easily. -If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes. +Additionally, GFM recognizes certain cross-project references, and also has a shorthand +version to reference other projects from the same namespace. -Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup: +GFM will recognize the following: -Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. +| references | input | cross-project reference | shortcut within same namespace | +| :------------------------------ | :------------------------- | :-------------------------------------- | :----------------------------- | +| specific user | `@user_name` | | | +| specific group | `@group_name` | | | +| entire team | `@all` | | | +| project | `namespace/project>` | | | +| issue | ``#123`` | `namespace/project#123` | `project#123` | +| merge request | `!123` | `namespace/project!123` | `project!123` | +| snippet | `$123` | `namespace/project$123` | `project$123` | +| epic **[ULTIMATE]** | `&123` | `group1/subgroup&123` | | +| label by ID | `~123` | `namespace/project~123` | `project~123` | +| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` | +| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` | +| project milestone by ID | `%123` | `namespace/project%123` | `project%123` | +| one-word milestone by name | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` | +| multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` | +| specific commit | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` | +| commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` | +| repository file references | `[README](doc/README)` | | | +| repository file line references | `[README](doc/README#L13)` | | | -On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support. +### Task lists + +> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#task-lists). + +You can add task lists anywhere markdown is supported, but you can only "click" to +toggle the boxes if they are in issues, merge requests, or comments. In other places +you must edit the markdown manually to change the status by adding or removing the `x`. -Ubuntu 18.04 (like many modern Linux distros) has this font installed by default. +To create a task list, add a specially-formatted Markdown list. You can use either +unordered or ordered lists: + +```markdown +- [x] Completed task +- [ ] Incomplete task + - [ ] Sub-task 1 + - [x] Sub-task 2 + - [ ] Sub-task 3 +1. [x] Completed task +1. [ ] Incomplete task + 1. [ ] Sub-task 1 + 1. [x] Sub-task 2 ``` -Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/monkey.png" width="20px" height="20px" style="display:inline;margin:0"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/star2.png" width="20px" height="20px" style="display:inline;margin:0"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speech_balloon.png" width="20px" height="20px" style="display:inline;margin:0">. Well we have a gift for you: +- [x] Completed task +- [ ] Incomplete task + - [ ] Sub-task 1 + - [x] Sub-task 2 + - [ ] Sub-task 3 +1. [x] Completed task +1. [ ] Incomplete task + 1. [ ] Sub-task 1 + 1. [x] Sub-task 2 -<img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/zap.png" width="20px" height="20px" style="display:inline;margin:0">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/v.png" width="20px" height="20px" style="display:inline;margin:0"> +### Wiki-specific Markdown -You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px" style="display:inline;margin:0"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px" style="display:inline;margin:0"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px" style="display:inline;margin:0"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px" style="display:inline;margin:0">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px" style="display:inline;margin:0"> you for that. +The following examples show how links inside wikis behave. -If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px" style="display:inline;margin:0">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px" style="display:inline;margin:0">. All you need to do is to look up one of the supported codes. +#### Wiki - Direct page link -Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px" style="display:inline;margin:0"> +A link which just includes the slug for a page will point to that page, +_at the base level of the wiki_. -Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. +This snippet would link to a `documentation` page at the root of your wiki: -On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support. +```markdown +[Link to Documentation](documentation) +``` -Ubuntu 18.04 (like many modern Linux distros) has this font installed by default. +#### Wiki - Direct file link -### Special GitLab references +Links with a file extension point to that file, _relative to the current page_. -GFM recognizes special references. +If the snippet below was placed on a page at `<your_wiki>/documentation/related`, +it would link to `<your_wiki>/documentation/file.md`: -You can easily reference e.g. an issue, a commit, a team member or even the whole team within a project. +```markdown +[Link to File](file.md) +``` -GFM will turn that reference into a link so you can navigate between them easily. +#### Wiki - Hierarchical link -GFM will recognize the following: +A link can be constructed relative to the current wiki page using `./<page>`, +`../<page>`, etc. -| input | references | -|:---------------------------|:--------------------------------| -| `@user_name` | specific user | -| `@group_name` | specific group | -| `@all` | entire team | -| `namespace/project>` | project | -| `#12345` | issue | -| `!123` | merge request | -| `$123` | snippet | -| `&123` | epic **[ULTIMATE]** | -| `~123` | label by ID | -| `~bug` | one-word label by name | -| `~"feature request"` | multi-word label by name | -| `%123` | project milestone by ID | -| `%v1.23` | one-word milestone by name | -| `%"release candidate"` | multi-word milestone by name | -| `9ba12248` | specific commit | -| `9ba12248...b19a04f5` | commit range comparison | -| `[README](doc/README)` | repository file references | -| `[README](doc/README#L13)` | repository file line references | - -GFM also recognizes certain cross-project references: - -| input | references | -|:----------------------------------------|:------------------------| -| `namespace/project#123` | issue | -| `namespace/project!123` | merge request | -| `namespace/project%123` | project milestone | -| `namespace/project$123` | snippet | -| `namespace/project@9ba12248` | specific commit | -| `group1/subgroup&123` | epic **[ULTIMATE]** | -| `namespace/project@9ba12248...b19a04f5` | commit range comparison | -| `namespace/project~"Some label"` | issues with given label | - -It also has a shorthand version to reference other projects from the same namespace: - -| input | references | -|:------------------------------|:------------------------| -| `project#123` | issue | -| `project!123` | merge request | -| `project%123` | project milestone | -| `project$123` | snippet | -| `project@9ba12248` | specific commit | -| `project@9ba12248...b19a04f5` | commit range comparison | -| `project~"Some label"` | issues with given label | +If this snippet was placed on a page at `<your_wiki>/documentation/main`, +it would link to `<your_wiki>/documentation/related`: -### Task lists +```markdown +[Link to Related Page](./related) +``` + +If this snippet was placed on a page at `<your_wiki>/documentation/related/content`, +it would link to `<your_wiki>/documentation/main`: -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#task-lists +```markdown +[Link to Related Page](../main) +``` -You can add task lists to issues, merge requests and comments. To create a task list, add a specially-formatted Markdown list, like so: +If this snippet was placed on a page at `<your_wiki>/documentation/main`, +it would link to `<your_wiki>/documentation/related.md`: +```markdown +[Link to Related Page](./related.md) ``` -- [x] Completed task -- [ ] Incomplete task - - [ ] Sub-task 1 - - [x] Sub-task 2 - - [ ] Sub-task 3 + +If this snippet was placed on a page at `<your_wiki>/documentation/related/content`, +it would link to `<your_wiki>/documentation/main.md`: + +```markdown +[Link to Related Page](../main.md) ``` -![alt unordered-check-list-render-gfm](img/unordered_check_list_render_gfm.png) +#### Wiki - Root link -Tasks formatted as ordered lists are supported as well: +A link starting with a `/` is relative to the wiki root. + +This snippet links to `<wiki_root>/documentation`: +```markdown +[Link to Related Page](/documentation) ``` -1. [x] Completed task -1. [ ] Incomplete task - 1. [ ] Sub-task 1 - 1. [x] Sub-task 2 + +This snippet links to `<wiki_root>/miscellaneous.md`: + +```markdown +[Link to Related Page](/miscellaneous.md) ``` -![alt task-list-ordered-render-gfm](img/task_list_ordered_render_gfm.png) +## Standard markdown and extensions in GitLab -Task lists can only be created in descriptions, not in titles. Task item state can be managed by editing the description's Markdown or by toggling the rendered check boxes. +All standard markdown formatting should work as expected within GitLab. Some standard +functionality is extended with additional features, without affecting the standard usage. +If a functionality is extended, the new option will be listed as a sub-section. -### Videos +### Blockquotes -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#videos +Blockquotes are an easy way to highlight information, such as a side-note. It is generated +by starting the lines of the blockquote with `>`: -Image tags with a video extension are automatically converted to a video player. +```markdown +> Blockquotes are very handy to emulate reply text. +> This line is part of the same quote. -The valid video extensions are `.mp4`, `.m4v`, `.mov`, `.webm`, and `.ogv`. +Quote break. - Here's a sample video: +> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. +``` - ![Sample Video](img/markdown_video.mp4) +> Blockquotes are very handy to emulate reply text. +> This line is part of the same quote. -Here's a sample video: +Quote break. -<div class="video-container"> - <video src="img/markdown_video.mp4" width="400" controls="true" data-setup="{}" data-title="Sample Video"></video> - <p><a href="img/markdown_video.mp4" target="_blank" rel="noopener noreferrer" title="Download 'Sample Video'">Sample Video</a></p> -</div> +> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. -### Math +#### Multiline blockquote -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#math +> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiline-blockquote). -It is possible to have math written with the LaTeX syntax rendered using [KaTeX][katex]. +GFM extends the standard markdown standard by also supporting multiline blockquotes +fenced by `>>>`: -Math written inside ```$``$``` will be rendered inline with the text. +``` +>>> +If you paste a message from somewhere else -Math written inside triple back quotes, with the language declared as `math`, will be rendered on a separate line. +that spans multiple lines, -Example: +you can quote that without having to manually prepend `>` to every line! +>>> +``` - This math is inline $`a^2+b^2=c^2`$. +>>> +If you paste a message from somewhere else - This is on a separate line - ```math - a^2+b^2=c^2 - ``` +that spans multiple lines, -Becomes: +you can quote that without having to manually prepend `>` to every line! +>>> -This math is inline ![alt text](img/math_inline_sup_render_gfm.png). +### Code spans and blocks -This is on a separate line +You can easily highlight anything that should be viewed as code and not simple text. -<img src="./img/math_inline_sup_render_gfm.png" > +Simple inline code is easily highlighted with single backticks `` ` ``: -_Be advised that KaTeX only supports a [subset][katex-subset] of LaTeX._ +```markdown +Inline `code` has `back-ticks around` it. +``` ->**Note:** -This also works for the asciidoctor `:stem: latexmath`. For details see the [asciidoctor user manual][asciidoctor-manual]. +Inline `code` has `back-ticks around` it. -### Colors +--- -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#colors +Similarly, a whole block of code can be fenced with triple backticks ```` ``` ````, +triple tildes (`~~~`), or indended 4 or more spaces to achieve a similar effect for +a larger body of code. test. -It is possible to have color written in HEX, RGB or HSL format rendered with a color indicator. +~~~ +``` +def function(): + #indenting works just fine in the fenced code block + s = "Python code" + print s +``` -Color written inside backticks will be followed by a color "chip". + Using 4 spaces + is like using + 3-backtick fences. +~~~ -Examples: +``` +~~~ +Tildes are OK too. +~~~ +``` - `#F00` - `#F00A` - `#FF0000` - `#FF0000AA` - `RGB(0,255,0)` - `RGB(0%,100%,0%)` - `RGBA(0,255,0,0.7)` - `HSL(540,70%,50%)` - `HSLA(540,70%,50%,0.7)` +The three examples above render as: -Becomes: +``` +def function(): + #indenting works just fine in the fenced code block + s = "Python code" + print s +``` -![alt color-inline-colorchip-render-gfm](img/color_inline_colorchip_render_gfm.png) + Using 4 spaces + is like using + 3-backtick fences. -#### Supported formats: +~~~ +Tildes are OK too. +~~~ -* HEX: `` `#RGB[A]` `` or `` `#RRGGBB[AA]` `` -* RGB: `` `RGB[A](R, G, B[, A])` `` -* HSL: `` `HSL[A](H, S, L[, A])` `` +#### Colored code and syntax highlighting -### Mermaid +> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#colored-code-and-syntax-highlighting). -> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107) in -GitLab 10.3. -> -> If this is not rendered correctly, see -https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#mermaid +GitLab uses the [Rouge Ruby library](http://rouge.jneen.net/) for more colorful syntax +highlighting in code blocks. For a list of supported languages visit the +[Rouge project wiki](https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers). +Syntax highlighting is only supported in code blocks, it is not possible to highlight +code when it is inline. -It is possible to generate diagrams and flowcharts from text using [Mermaid](https://mermaidjs.github.io/). +Blocks of code are fenced by lines with three back-ticks ```` ``` ```` or three tildes `~~~`, and have +the language identified at the end of the first fence: -In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block. +~~~ +```javascript +var s = "JavaScript syntax highlighting"; +alert(s); +``` -Example: +```python +def function(): + #indenting works just fine in the fenced code block + s = "Python syntax highlighting" + print s +``` - ```mermaid - graph TD; - A-->B; - A-->C; - B-->D; - C-->D; - ``` +```ruby +require 'redcarpet' +markdown = Redcarpet.new("Hello World!") +puts markdown.to_html +``` -Becomes: +``` +No language indicated, so no syntax highlighting. +s = "There is no highlighting for this." +But let's throw in a <b>tag</b>. +``` +~~~ -```mermaid -graph TD; - A-->B; - A-->C; - B-->D; - C-->D; +The four examples above render as: + +```javascript +var s = "JavaScript syntax highlighting"; +alert(s); ``` -For details see the [Mermaid official page](https://mermaidjs.github.io/). +```python +def function(): + #indenting works just fine in the fenced code block + s = "Python syntax highlighting" + print s +``` -### Front matter +```ruby +require 'redcarpet' +markdown = Redcarpet.new("Hello World!") +puts markdown.to_html +``` -> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23331) - in GitLab 11.6. +``` +No language indicated, so no syntax highlighting. +s = "There is no highlighting for this." +But let's throw in a <b>tag</b>. +``` -Front matter is metadata included at the beginning of a markdown document, preceding -its content. This data can be used by static site generators such as [Jekyll](https://jekyllrb.com/docs/front-matter/) and [Hugo](https://gohugo.io/content-management/front-matter/), -and many other applications. +### Emphasis -In GitLab, front matter is only used in Markdown files and wiki pages, not the other places where Markdown formatting is supported. -When you view a Markdown file rendered by GitLab, any front matter is displayed as-is, in a box at the top of the document, before the rendered HTML content. -To view an example, you can toggle between the source and rendered version of a [GitLab documentation file](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/README.md). +There are multiple ways to emphasize text in markdown. You can italicize, bold, strikethrough, +as well as combine these emphasis styles together. -The following delimeters are supported: +Examples: -- YAML (`---`): +```markdown +Emphasis, aka italics, with *asterisks* or _underscores_. - ``` - --- - title: About Front Matter - example: - language: yaml - --- - ``` +Strong emphasis, aka bold, with double **asterisks** or __underscores__. -- TOML (`+++`): +Combined emphasis with **asterisks and _underscores_**. - ``` - +++ - title = "About Front Matter" - [example] - language = "toml" - +++ - ``` +Strikethrough uses two tildes. ~~Scratch this.~~ +``` -- JSON (`;;;`): +Emphasis, aka italics, with *asterisks* or _underscores_. - ``` - ;;; - { - "title": "About Front Matter" - "example": { - "language": "json" - } - } - ;;; - ``` +Strong emphasis, aka bold, with double **asterisks** or __underscores__. -Other languages are supported by adding a specifier to any of the existing -delimiters. For example: +Combined emphasis with **asterisks and _underscores_**. + +Strikethrough uses two tildes. ~~Scratch this.~~ +NOTE: **Note:** Strikethrough is not part of the core Markdown standard, but is part of GFM. + +#### Multiple underscores in words and mid-word emphasis + +> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiple-underscores-in-words). + +It is not usually useful to italicize just _part_ of a word, especially when you're +dealing with code and names that often appear with multiple underscores. As a result, +GFM extends the standard markdown standard by ignoring multiple underlines in words, +to allow better rendering of markdown documents discussing code: + +```md +perform_complicated_task + +do_this_and_do_that_and_another_thing + +but_emphasis is_desired _here_ ``` ----php -$title = "About Front Matter"; -$example = array( - 'language' => "php", -); + +perform_complicated_task + +do_this_and_do_that_and_another_thing + +but_emphasis is_desired _here_ + --- + +If you wish to emphasize only a part of a word, it can still be done with asterisks: + +```md +perform*complicated*task + +do*this*and*do*that*and*another thing ``` -## Standard Markdown +perform*complicated*task + +do*this*and*do*that*and*another thing -### Headers +### Footnotes + +Footnotes add a link to a note rendered at the end of a markdown file: + +```markdown +You can add footnotes to your text as follows.[^1] +[^1]: This is my awesome footnote (later in file). ``` + +You can add footnotes to your text as follows.[^1] + +[^1]: This is my awesome footnote (later in file). + +### Headers + +```markdown # H1 ## H2 ### H3 @@ -593,9 +758,11 @@ Alt-H2 #### Header IDs and links -All Markdown-rendered headers automatically get IDs, which can be linked to, except in comments. +GFM extends the standard markdown standard so that all Markdown-rendered headers automatically +get IDs, which can be linked to, except in comments. -On hover, a link to those IDs becomes visible to make it easier to copy the link to the header to use it somewhere else. +On hover, a link to those IDs becomes visible to make it easier to copy the link to +the header to use it somewhere else. The IDs are generated from the content of the header according to the following rules: @@ -606,7 +773,7 @@ The IDs are generated from the content of the header according to the following 1. If a header with the same ID has already been generated, a unique incrementing number is appended, starting at 1. -For example: +Example: ``` # This header has spaces in it @@ -626,215 +793,164 @@ Would generate the following link IDs: 1. `this-header-has-spaces-in-it-2` 1. `this-header-has-3-5-in-it-and-parentheses` -Note that the Emoji processing happens before the header IDs are generated, so the Emoji is converted to an image which then gets removed from the ID. - -### Emphasis - -Examples: - -``` -Emphasis, aka italics, with *asterisks* or _underscores_. - -Strong emphasis, aka bold, with **asterisks** or __underscores__. - -Combined emphasis with **asterisks and _underscores_**. - -Strikethrough uses two tildes. ~~Scratch this.~~ -``` - -Becomes: - -Emphasis, aka italics, with *asterisks* or _underscores_. - -Strong emphasis, aka bold, with **asterisks** or __underscores__. - -Combined emphasis with **asterisks and _underscores_**. - -Strikethrough uses two tildes. ~~Scratch this.~~ - -### Lists - -Examples: - -``` -1. First ordered list item -2. Another item - * Unordered sub-list. -1. Actual numbers don't matter, just that it's a number - 1. Ordered sub-list -4. And another item. - -* Unordered list can use asterisks -- Or minuses -+ Or pluses -``` - -Becomes: - -1. First ordered list item -2. Another item - * Unordered sub-list. -1. Actual numbers don't matter, just that it's a number - 1. Ordered sub-list -4. And another item. - -* Unordered list can use asterisks -- Or minuses -+ Or pluses - -If a list item contains multiple paragraphs, -each subsequent paragraph should be indented to the same level as the start of the list item text +Note that the Emoji processing happens before the header IDs are generated, so the +Emoji is converted to an image which is then removed from the ID. -Example: - -``` -1. First ordered list item - - Second paragraph of first item. - -2. Another item -``` +### Horizontal Rule -Becomes: +It's very simple to create a horizontal rule, by using three or more hyphens, asterisks, +or underscores: -1. First ordered list item +```markdown +Three or more hyphens, - Paragraph of first item. +--- -2. Another item +asterisks, -If the paragraph of the first item is not indented with the proper number of spaces, -the paragraph will appear outside the list, instead of properly indented under the list item. +*** -Example: +or underscores +___ ``` -1. First ordered list item - Paragraph of first item. +Three or more hyphens, -2. Another item -``` +--- -Becomes: +asterisks, -1. First ordered list item +*** - Paragraph of first item. +or underscores -2. Another item +___ -### Links +### Images -There are two ways to create links, inline-style and reference-style. +Examples: ```markdown -[I'm an inline-style link](https://www.google.com) -[I'm a link to a repository file in the same directory](index.md) -[I am an absolute reference within the repository](/doc/user/index.md) -[I'm a relative link to the Milestones page](../README.md) +Inline-style (hover to see title text): -[I link to a section on a different markdown page, using a header ID](index.md#overview) -[I link to a different section on the same page, using the header ID](#header-ids-and-links) +![alt text](img/markdown_logo.png "Title Text") -[I'm a reference-style link][Arbitrary case-insensitive reference text] -[You can use numbers for reference-style link definitions][1] -Or leave it empty and use the [link text itself][] +Reference-style (hover to see title text): -Some text to show that the reference links can follow later. +![alt text1][logo] -[arbitrary case-insensitive reference text]: https://www.mozilla.org -[1]: http://slashdot.org -[link text itself]: https://www.reddit.com +[logo]: img/markdown_logo.png "Title Text" ``` ->**Note:** -Relative links do not allow referencing project files in a wiki page or wiki -page in a project file. The reason for this is that, in GitLab, wiki is always -a separate Git repository. For example, `[I'm a reference-style link](style)` -will point the link to `wikis/style` when the link is inside of a wiki markdown file. - -### Images +Inline-style (hover to see title text): -Examples: +![alt text](img/markdown_logo.png "Title Text") - Here's our logo (hover to see the title text): +Reference-style (hover to see title text): - Inline-style: - ![alt text](img/markdown_logo.png) +![alt text][logo] - Reference-style: - ![alt text1][logo] +[logo]: img/markdown_logo.png "Title Text" - [logo]: img/markdown_logo.png +#### Videos -Becomes: +> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#videos). -Here's our logo: +Image tags that link to files with a video extension are automatically converted to +a video player. The valid video extensions are `.mp4`, `.m4v`, `.mov`, `.webm`, and `.ogv`: -Inline-style: +```md +Here's a sample video: -![alt text](img/markdown_logo.png) +![Sample Video](img/markdown_video.mp4) +``` -Reference-style: +Here's a sample video: -![alt text][logo] +![Sample Video](img/markdown_video.mp4) -[logo]: img/markdown_logo.png +### Inline HTML -### Blockquotes +> To see the markdown rendered within HTML in the second example, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-html). -Examples: +You can also use raw HTML in your Markdown, and it'll usually work pretty well. -``` -> Blockquotes are very handy in email to emulate reply text. -> This line is part of the same quote. +See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/1.11.0/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) +class for the list of allowed HTML tags and attributes. In addition to the default +`SanitizationFilter` whitelist, GitLab allows `span`, `abbr`, `details` and `summary` elements. -Quote break. +```html +<dl> + <dt>Definition list</dt> + <dd>Is something people use sometimes.</dd> -> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. + <dt>Markdown in HTML</dt> + <dd>Does *not* work **very** well. HTML <em>tags</em> will <b>always</b> work.</dd> +</dl> ``` -Becomes: - -> Blockquotes are very handy in email to emulate reply text. -> This line is part of the same quote. - -Quote break. - -> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. - -### Inline HTML +<dl> + <dt>Definition list</dt> + <dd>Is something people use sometimes.</dd> -You can also use raw HTML in your Markdown, and it'll mostly work pretty well. + <dt>Markdown in HTML</dt> + <dd>Does *not* work **very** well. HTML <em>tags</em> will <b>always</b> work.</dd> +</dl> -See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/1.11.0/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows `span`, `abbr`, `details` and `summary` elements. +--- -Examples: +It is still possible to use markdown inside HTML tags, but only if the lines containing markdown +are separated into their own lines: -``` +```html <dl> - <dt>Definition list</dt> - <dd>Is something people use sometimes.</dd> + <dt>Markdown in HTML</dt> + <dd>Does *not* work **very** well. HTML tags will always work.</dd> <dt>Markdown in HTML</dt> - <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd> + <dd> + + Does *not* work **very** well. HTML tags will always work. + + </dd> </dl> ``` -Becomes: +<!-- Note: The example below uses HTML to force correct rendering on docs.gitlab.com, markdown will be fine in GitLab --> <dl> - <dt>Definition list</dt> - <dd>Is something people use sometimes.</dd> + <dt>Markdown in HTML</dt> + <dd>Does *not* work **very** well. HTML tags will always work.</dd> <dt>Markdown in HTML</dt> - <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd> + <dd> + + Does <em>not</em> work <b>very</b> well. HTML tags will always work. + + </dd> </dl> #### Details and Summary -Content can be collapsed using HTML's [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) and [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary) tags. This is especially useful for collapsing long logs so they take up less screen space. +> To see the markdown rendered within HTML in the second example, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#details-and-summary). + +Content can be collapsed using HTML's [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) +and [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary) +tags. This is especially useful for collapsing long logs so they take up less screen space. + +```html +<p> +<details> +<summary>Click me to collapse/fold.</summary> + +These details <em>will</em> remain <strong>hidden</strong> until expanded. + +<pre><code>PASTE LOGS HERE</code></pre> + +</details> +</p> +``` <p> <details> @@ -847,7 +963,10 @@ These details <em>will</em> remain <strong>hidden</strong> until expanded. </details> </p> -**Note:** Markdown inside these tags is supported, as long as you have a blank line after the `</summary>` tag and before the `</details>` tag, as shown in the example. +--- + +Markdown inside these tags is supported as well, as long as you have a blank line +after the `</summary>` tag and before the `</details>` tag, as shown in the example: ```html <details> @@ -860,232 +979,302 @@ These details _will_ remain **hidden** until expanded. </details> ``` -### Horizontal Rule +<!-- Note: The example below uses HTML to force correct rendering on docs.gitlab.com, markdown will be fine in GitLab --> -Examples: +<details> +<summary>Click me to collapse/fold.</summary> -``` -Three or more... +These details <em>will</em> remain <b>hidden</b> until expanded. ---- + PASTE LOGS HERE + +</details> -Hyphens +### Line Breaks -*** +A line break will be inserted (a new paragraph will start) if the previous text is +ended with two newlines, i.e. you hit <kbd>Enter</kbd> twice in a row. If you only +use one newline (hit <kbd>Enter</kbd> once), the next sentence will be part of the +same paragraph. This is useful if you want to keep long lines from wrapping, and keep +them easily editable: -Asterisks +```markdown +Here's a line for us to start with. -___ +This longer line is separated from the one above by two newlines, so it will be a *separate paragraph*. -Underscores +This line is also a separate paragraph, but... +These lines are only separated by single newlines, +so they *do not break* and just follow the previous lines +in the *same paragraph*. ``` -Becomes: +Here's a line for us to start with. -Three or more... +This longer line is separated from the one above by two newlines, so it will be a *separate paragraph*. ---- +This line is also a separate paragraph, but... +These lines are only separated by single newlines, +so they *do not break* and just follow the previous lines +in the *same paragraph*. -Hyphens +#### Newlines -*** +GFM adheres to the markdown specification in how [paragraphs and line breaks are handled](https://spec.commonmark.org/current/). -Asterisks +A paragraph is simply one or more consecutive lines of text, separated by one or +more blank lines (i.e. two newlines at the end of the first paragraph), as [explained above](#line-breaks). -___ +If you need more control over line-breaks or soft returns, you can add a single line-break +by ending a line with a backslash, or two or more spaces. Two newlines in a row will create a new +paragraph, with a blank line in between: -Underscores +```markdown +First paragraph. +Another line in the same paragraph. +A third line in the same paragraph, but this time ending with two spaces.{space}{space} +A new line directly under the first paragraph. + +Second paragraph. +Another line, this time ending with a backslash.\ +A new line due to the previous backslash. +``` -### Line Breaks +<!-- (Do *NOT* remove the two ending whitespaces in the third line) --> +<!-- (They are needed for the Markdown text to render correctly) --> -A good way to learn how line breaks work is to experiment and discover -- hit <kbd>Enter</kbd> once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. The "Preview" tab is your friend. +First paragraph. +Another line in the same paragraph. +A third line in the same paragraph, but this time ending with two spaces. +A new line directly under the first paragraph. -Here are some things to try out: +<!-- (Do *NOT* remove the two ending whitespaces in the second line) --> +<!-- (They are needed for the Markdown text to render correctly on docs.gitlab.com, the backslash works fine inside GitLab itself) --> -Examples: +Second paragraph. +Another line, this time ending with a backslash. +A new line due to the previous backslash. -``` -Here's a line for us to start with. +### Links -This line is separated from the one above by two newlines, so it will be a *separate paragraph*. +There are two ways to create links, inline-style and reference-style: -This line is also a separate paragraph, but... -This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*. +```md +- This is an [inline-style link](https://www.google.com) +- This is a [link to a repository file in the same directory](index.md) +- This is a [relative link to a readme one directory higher](../README.md) +- This is a [link that also has title text](https://www.google.com "This link takes you to Google!") -This line is also a separate paragraph, and... -This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*) +Using header ID anchors: -spaces. -``` +- This links to [a section on a different markdown page, using a "#" and the header ID](index.md#overview) +- This links to [a different section on the same page, using a "#" and the header ID](#header-ids-and-links) -Becomes: +Using references: -Here's a line for us to start with. +- This is a [reference-style link, see below][Arbitrary case-insensitive reference text] +- You can [use numbers for reference-style link definitions, see below][1] +- Or leave it empty and use the [link text itself][], see below. -This line is separated from the one above by two newlines, so it will be a *separate paragraph*. +Some text to show that the reference links can follow later. -This line is also a separate paragraph, but... -This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*. +[arbitrary case-insensitive reference text]: https://www.mozilla.org +[1]: http://slashdot.org +[link text itself]: https://www.reddit.com +``` -This line is also a separate paragraph, and... -This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*) +- This is an [inline-style link](https://www.google.com) +- This is a [link to a repository file in the same directory](index.md) +- This is a [relative link to a readme one directory higher](../README.md) +- This is a [link that also has title text](https://www.google.com "This link takes you to Google!") -spaces. +Using header ID anchors: -### Tables +- This links to [a section on a different markdown page, using a "#" and the header ID](index.md#overview) +- This links to [a different section on the same page, using a "#" and the header ID](#header-ids-and-links) -Tables aren't part of the core Markdown spec, but they are part of GFM. +Using references: -Example: +- This is a [reference-style link, see below][Arbitrary case-insensitive reference text] +- You can [use numbers for reference-style link definitions, see below][1] +- Or leave it empty and use the [link text itself][], see below. -``` -| header 1 | header 2 | -| -------- | -------- | -| cell 1 | cell 2 | -| cell 3 | cell 4 | -``` - -Becomes: +Some text to show that the reference links can follow later. -| header 1 | header 2 | -| -------- | -------- | -| cell 1 | cell 2 | -| cell 3 | cell 4 | +[arbitrary case-insensitive reference text]: https://www.mozilla.org +[1]: http://slashdot.org +[link text itself]: https://www.reddit.com -**Note:** The row of dashes between the table header and body must have at least three dashes in each column. +NOTE: **Note:** Relative links do not allow the referencing of project files in a wiki +page, or a wiki page in a project file. The reason for this is that a wiki is always +in a separate Git repository in GitLab. For example, `[I'm a reference-style link](style)` +will point the link to `wikis/style` only when the link is inside of a wiki markdown file. -By including colons in the header row, you can align the text within that column. +#### URL auto-linking -Example: +GFM will autolink almost any URL you put into your text: -``` -| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned | -| :----------- | :------: | ------------: | :----------- | :------: | ------------: | -| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 | -| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 | +```markdown +* https://www.google.com +* https://google.com/ +* ftp://ftp.us.debian.org/debian/ +* smb://foo/bar/baz +* irc://irc.freenode.net/gitlab +* http://localhost:3000 ``` -Becomes: +* https://www.google.com +* https://google.com/ +* ftp://ftp.us.debian.org/debian/ +* smb://foo/bar/baz +* irc://irc.freenode.net/gitlab +* http://localhost:3000 -| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned | -| :----------- | :------: | ------------: | :----------- | :------: | ------------: | -| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 | -| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 | +### Lists -### Footnotes +Ordered and unordered lists can be easily created. Add the number you want the list +to start with, like `1. ` (with a space) at the start of each line for ordered lists. +After the first number, it does not matter what number you use, ordered lists will be +numbered automatically by vertical order, so repeating `1. ` for all items in the +same list is common. If you start with a number other than `1. `, it will use that as the first +number, and count up from there. -Example: +Add a `* `, `- ` or `+ ` (with a space) at the start of each line for unordered lists, but +you should not use a mix of them. -``` -You can add footnotes to your text as follows.[^2] -[^2]: This is my awesome footnote. +Examples: + +```md +1. First ordered list item +2. Another item + * Unordered sub-list. +1. Actual numbers don't matter, just that it's a number + 1. Ordered sub-list + 1. Next ordered sub-list item +4. And another item. + +* Unordered lists can use asterisks +- Or minuses ++ Or pluses ``` -Becomes: +1. First ordered list item +2. Another item + * Unordered sub-list. +1. Actual numbers don't matter, just that it's a number + 1. Ordered sub-list + 1. Next ordered sub-list item +4. And another item. -You can add footnotes to your text as follows.[^2] +* Unordered lists can use asterisks +- Or minuses ++ Or pluses -### Superscripts / Subscripts +--- -CommonMark and GFM currently do not support the superscript syntax ( `x^2` ) that Redcarpet does. You can use the standard HTML syntax for superscripts and subscripts. +If a list item contains multiple paragraphs, each subsequent paragraph should be indented +to the same level as the start of the list item text. -``` -The formula for water is H<sub>2</sub>O -while the equation for the theory of relativity is E = mc<sup>2</sup>. -``` +Example: -The formula for water is H<sub>2</sub>O while the equation for the theory of relativity is E = mc<sup>2</sup>. +```markdown +1. First ordered list item -## Wiki-specific Markdown + Second paragraph of first item. -The following examples show how links inside wikis behave. +2. Another item +``` -### Wiki - Direct page link +1. First ordered list item -A link which just includes the slug for a page will point to that page, -_at the base level of the wiki_. + Second paragraph of first item. -This snippet would link to a `documentation` page at the root of your wiki: +2. Another item -```markdown -[Link to Documentation](documentation) -``` +--- -### Wiki - Direct file link +If the paragraph of the first item is not indented with the proper number of spaces, +the paragraph will appear outside the list, instead of properly indented under the list item. -Links with a file extension point to that file, _relative to the current page_. +Example: -If this snippet was placed on a page at `<your_wiki>/documentation/related`, -it would link to `<your_wiki>/documentation/file.md`: +``` +1. First ordered list item -```markdown -[Link to File](file.md) + Paragraph of first item. + +2. Another item ``` -### Wiki - Hierarchical link +1. First ordered list item -A link can be constructed relative to the current wiki page using `./<page>`, -`../<page>`, etc. + Paragraph of first item. -- If this snippet was placed on a page at `<your_wiki>/documentation/main`, - it would link to `<your_wiki>/documentation/related`: +2. Another item - ```markdown - [Link to Related Page](related) - ``` +### Superscripts / Subscripts -- If this snippet was placed on a page at `<your_wiki>/documentation/related/content`, - it would link to `<your_wiki>/documentation/main`: +CommonMark and GFM currently do not support the superscript syntax ( `x^2` ) that +Redcarpet does. You can use the standard HTML syntax for superscripts and subscripts: - ```markdown - [Link to Related Page](../main) - ``` +```html +The formula for water is H<sub>2</sub>O +while the equation for the theory of relativity is E = mc<sup>2</sup>. +``` -- If this snippet was placed on a page at `<your_wiki>/documentation/main`, - it would link to `<your_wiki>/documentation/related.md`: +The formula for water is H<sub>2</sub>O +while the equation for the theory of relativity is E = mc<sup>2</sup>. - ```markdown - [Link to Related Page](related.md) - ``` +### Tables -- If this snippet was placed on a page at `<your_wiki>/documentation/related/content`, - it would link to `<your_wiki>/documentation/main.md`: +Tables aren't part of the core Markdown spec, but they are part of GFM. - ```markdown - [Link to Related Page](../main.md) - ``` +1. The first line contains the headers, separated by "pipes" (`|`). +1. The second line separates the headers from the cells, and must contain three or more dashes. +1. The third, and any following lines, contain the cell values. + - You **can't** have cells separated over many lines in the markdown, they must be kept to single lines, + but they can be very long. You can also include HTML `<br>` tags to force newlines if needed. + - The cell sizes **don't** have to match each other. They are flexible, but must be separated + by pipes (`|`). + - You **can** have blank cells. -### Wiki - Root link +Example: -A link starting with a `/` is relative to the wiki root. +```markdown +| header 1 | header 2 | header 3 | +| --- | ------ |----------| +| cell 1 | cell 2 | cell 3 | +| cell 4 | cell 5 is longer | cell 6 is much longer than the others, but that's ok. It will eventually wrap the text when the cell is too large for the display size. | +| cell 7 | | cell <br> 9 | +``` -- This snippet links to `<wiki_root>/documentation`: +| header 1 | header 2 | header 3 | +| --- | ------ |---------:| +| cell 1 | cell 2 | cell 3 | +| cell 4 | cell 5 is longer | cell 6 is much longer than the others, but that's ok. It will eventually wrap the text when the cell is too large for the display size. | +| cell 7 | | cell <br> 9 | - ```markdown - [Link to Related Page](/documentation) - ``` +Additionally, you can choose the alignment of text within columns by adding colons (`:`) +to the sides of the "dash" lines in the second row. This will affect every cell in the column. -- This snippet links to `<wiki_root>/miscellaneous.md`: +> Note that the headers are always right aligned [within GitLab itself itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#tables). - ```markdown - [Link to Related Page](/miscellaneous.md) - ``` +```markdown +| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned | +| :--- | :---: | ---: | :----------- | :------: | ------------: | +| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 | +| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 | +``` + +| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned | +| :--- | :---: | ---: | :----------- | :------: | ------------: | +| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 | +| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 | ## References - This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). -- The original [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown. -- The detailed specification for CommonMark can be found in the [CommonMark Spec][commonmark-spec] +- The original [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) + at Daring Fireball is an excellent resource for a detailed explanation of standard markdown. +- The detailed specification for CommonMark can be found in the [CommonMark Spec](https://spec.commonmark.org/current/) - The [CommonMark Dingus](http://try.commonmark.org) is a handy tool for testing CommonMark syntax. - -[^1]: This link will be broken if you see this document from the Help page or docs.gitlab.com -[^2]: This is my awesome footnote. - -[markdown.md]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md -[rouge]: http://rouge.jneen.net/ "Rouge website" -[redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website" -[katex]: https://github.com/Khan/KaTeX "KaTeX website" -[katex-subset]: https://katex.org/docs/supported.html "Macros supported by KaTeX" -[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual" -[commonmarker]: https://github.com/gjtorikian/commonmarker -[commonmark-spec]: https://spec.commonmark.org/current/ diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index be4215b1934..2bf8d4dfe7b 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -18,7 +18,7 @@ Adjust your project's name, description, avatar, [default branch](../repository/ ![general project settings](img/general_settings.png) -The project description also partially supports [standard markdown](../../markdown.md#standard-markdown). You can use [emphasis](../../markdown.md#emphasis), [links](../../markdown.md#links), and [line-breaks](../../markdown.md#line-breaks) to add more context to the project description. +The project description also partially supports [standard markdown](../../markdown.md#standard-markdown-and-extensions-in-gitlab). You can use [emphasis](../../markdown.md#emphasis), [links](../../markdown.md#links), and [line-breaks](../../markdown.md#line-breaks) to add more context to the project description. ### Sharing and permissions diff --git a/lib/gitlab.rb b/lib/gitlab.rb index fd4bbd69468..c62d1071dba 100644 --- a/lib/gitlab.rb +++ b/lib/gitlab.rb @@ -71,6 +71,10 @@ module Gitlab end end + def self.ee + yield if ee? + end + def self.http_proxy_env? HTTP_PROXY_ENV_VARS.any? { |name| ENV[name] } end diff --git a/lib/gitlab/git/raw_diff_change.rb b/lib/gitlab/git/raw_diff_change.rb index e1002af40f6..9a41f04a4db 100644 --- a/lib/gitlab/git/raw_diff_change.rb +++ b/lib/gitlab/git/raw_diff_change.rb @@ -11,8 +11,8 @@ module Gitlab if raw_change.is_a?(Gitaly::GetRawChangesResponse::RawChange) @blob_id = raw_change.blob_id @blob_size = raw_change.size - @old_path = raw_change.old_path.presence - @new_path = raw_change.new_path.presence + @old_path = raw_change.old_path_bytes.presence + @new_path = raw_change.new_path_bytes.presence @operation = raw_change.operation&.downcase || :unknown else parse(raw_change) diff --git a/lib/gitlab/metrics/dashboard/base_service.rb b/lib/gitlab/metrics/dashboard/base_service.rb index 90895eb237a..0628e82e592 100644 --- a/lib/gitlab/metrics/dashboard/base_service.rb +++ b/lib/gitlab/metrics/dashboard/base_service.rb @@ -10,6 +10,8 @@ module Gitlab NOT_FOUND_ERROR = Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError def get_dashboard + return error('Insufficient permissions.', :unauthorized) unless allowed? + success(dashboard: process_dashboard) rescue NOT_FOUND_ERROR error("#{dashboard_path} could not be found.", :not_found) @@ -30,6 +32,12 @@ module Gitlab private + # Determines whether users should be able to view + # dashboards at all. + def allowed? + Ability.allowed?(current_user, :read_environment, project) + end + # Returns a new dashboard Hash, supplemented with DB info def process_dashboard Gitlab::Metrics::Dashboard::Processor diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb index a07b1246bee..a13b3f9e069 100644 --- a/lib/gitlab/path_regex.rb +++ b/lib/gitlab/path_regex.rb @@ -53,7 +53,6 @@ module Gitlab sent_notifications slash-command-logo.png snippets - u unsubscribes uploads users diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2da0633f4cc..9ea368816f9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5545,6 +5545,9 @@ msgstr "" msgid "IssueBoards|Board" msgstr "" +msgid "IssueBoards|Boards" +msgstr "" + msgid "Issues" msgstr "" diff --git a/package.json b/package.json index 577967d23b9..ce3e5bd4490 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "jszip-utils": "^0.0.2", "katex": "^0.10.0", "marked": "^0.3.12", - "mermaid": "^8.0.0", + "mermaid": "^8.1.0", "monaco-editor": "^0.15.6", "monaco-editor-webpack-plugin": "^1.7.0", "mousetrap": "^1.4.6", diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb index 2750b171a85..567c6a83ddf 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb @@ -2,8 +2,7 @@ module QA context 'Create' do - # Issue: https://gitlab.com/gitlab-org/quality/nightly/issues/97 - describe 'File templates', :quarantine do + describe 'File templates' do include Runtime::Fixtures def login diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb index 078d3b2b5b1..c09c65a57a5 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true module QA - # Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/46 - context 'Create', :quarantine do + context 'Create' do describe 'Web IDE file templates' do include Runtime::Fixtures diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb index 881d0018b79..5e0f64ccca4 100644 --- a/spec/controllers/groups/boards_controller_spec.rb +++ b/spec/controllers/groups/boards_controller_spec.rb @@ -59,7 +59,7 @@ describe Groups::BoardsController do it 'return an array with one group board' do create(:board, group: group) - expect(Boards::Visits::LatestService).not_to receive(:new) + expect(Boards::VisitsFinder).not_to receive(:new) list_boards format: :json diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb index ae85000b4e0..c07afc57aea 100644 --- a/spec/controllers/projects/boards_controller_spec.rb +++ b/spec/controllers/projects/boards_controller_spec.rb @@ -65,7 +65,7 @@ describe Projects::BoardsController do it 'returns a list of project boards' do create_list(:board, 2, project: project) - expect(Boards::Visits::LatestService).not_to receive(:new) + expect(Boards::VisitsFinder).not_to receive(:new) list_boards format: :json diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 4634d1d4bb3..5a5c0a1f6ac 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -17,6 +17,10 @@ describe SearchController do set(:project) { create(:project, :public, :repository, :wiki_repo) } + before do + expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original + end + subject { get(:show, params: { project_id: project.id, scope: scope, search: 'merge' }) } where(:partial, :scope) do @@ -35,6 +39,19 @@ describe SearchController do end end + context 'global search' do + render_views + + it 'omits pipeline status from load' do + project = create(:project, :public) + expect(Gitlab::Cache::Ci::ProjectPipelineStatus).not_to receive(:load_in_batch_for_projects) + + get :show, params: { scope: 'projects', search: project.name } + + expect(assigns[:search_objects].first).to eq project + end + end + it 'finds issue comments' do project = create(:project, :public) note = create(:note_on_issue, project: project) diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb index 7a3b1d7ed47..ea720cee74e 100644 --- a/spec/features/discussion_comments/commit_spec.rb +++ b/spec/features/discussion_comments/commit_spec.rb @@ -6,6 +6,8 @@ describe 'Discussion Comments Commit', :js do let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } + let!(:commit_discussion_note1) { create(:discussion_note_on_commit, project: project) } + let!(:commit_discussion_note2) { create(:discussion_note_on_commit, in_reply_to: commit_discussion_note1) } before do project.add_maintainer(user) @@ -15,4 +17,18 @@ describe 'Discussion Comments Commit', :js do end it_behaves_like 'discussion comments', 'commit' + + it 'has class .js-note-emoji' do + expect(page).to have_css('.js-note-emoji') + end + + it 'adds award to the correct note' do + find("#note_#{commit_discussion_note2.id} .js-note-emoji").click + first('.emoji-menu .js-emoji-btn').click + + wait_for_requests + + expect(find("#note_#{commit_discussion_note1.id}")).not_to have_css('.js-awards-block') + expect(find("#note_#{commit_discussion_note2.id}")).to have_css('.js-awards-block') + end end diff --git a/spec/services/boards/visits/latest_service_spec.rb b/spec/finders/boards/visits_finder_spec.rb index c8a0a5e4243..4d40f4826f8 100644 --- a/spec/services/boards/visits/latest_service_spec.rb +++ b/spec/finders/boards/visits_finder_spec.rb @@ -2,32 +2,32 @@ require 'spec_helper' -describe Boards::Visits::LatestService do - describe '#execute' do +describe Boards::VisitsFinder do + describe '#latest' do let(:user) { create(:user) } context 'when a project board' do let(:project) { create(:project) } let(:project_board) { create(:board, project: project) } - subject(:service) { described_class.new(project_board.parent, user) } + subject(:finder) { described_class.new(project_board.parent, user) } it 'returns nil when there is no user' do - service.current_user = nil + finder.current_user = nil - expect(service.execute).to eq nil + expect(finder.execute).to eq nil end it 'queries for most recent visit' do expect(BoardProjectRecentVisit).to receive(:latest).once - service.execute + finder.execute end it 'queries for last N visits' do expect(BoardProjectRecentVisit).to receive(:latest).with(user, project, count: 5).once - described_class.new(project_board.parent, user, count: 5).execute + described_class.new(project_board.parent, user).latest(5) end end @@ -35,24 +35,24 @@ describe Boards::Visits::LatestService do let(:group) { create(:group) } let(:group_board) { create(:board, group: group) } - subject(:service) { described_class.new(group_board.parent, user) } + subject(:finder) { described_class.new(group_board.parent, user) } it 'returns nil when there is no user' do - service.current_user = nil + finder.current_user = nil - expect(service.execute).to eq nil + expect(finder.execute).to eq nil end it 'queries for most recent visit' do expect(BoardGroupRecentVisit).to receive(:latest).once - service.execute + finder.latest end it 'queries for last N visits' do expect(BoardGroupRecentVisit).to receive(:latest).with(user, group, count: 5).once - described_class.new(group_board.parent, user, count: 5).execute + described_class.new(group_board.parent, user).latest(5) end end end diff --git a/spec/frontend/notes/components/discussion_notes_spec.js b/spec/frontend/notes/components/discussion_notes_spec.js index c3204b3aaa0..394666403ee 100644 --- a/spec/frontend/notes/components/discussion_notes_spec.js +++ b/spec/frontend/notes/components/discussion_notes_spec.js @@ -112,6 +112,44 @@ describe('DiscussionNotes', () => { }); }); + describe('events', () => { + describe('with groupped notes and replies expanded', () => { + const findNoteAtIndex = index => wrapper.find(`.note:nth-of-type(${index + 1}`); + + beforeEach(() => { + createComponent({ shouldGroupReplies: true, isExpanded: true }); + }); + + it('emits deleteNote when first note emits handleDeleteNote', () => { + findNoteAtIndex(0).vm.$emit('handleDeleteNote'); + expect(wrapper.emitted().deleteNote).toBeTruthy(); + }); + + it('emits startReplying when first note emits startReplying', () => { + findNoteAtIndex(0).vm.$emit('startReplying'); + expect(wrapper.emitted().startReplying).toBeTruthy(); + }); + + it('emits deleteNote when second note emits handleDeleteNote', () => { + findNoteAtIndex(1).vm.$emit('handleDeleteNote'); + expect(wrapper.emitted().deleteNote).toBeTruthy(); + }); + }); + + describe('with ungroupped notes', () => { + let note; + beforeEach(() => { + createComponent(); + note = wrapper.find('.note'); + }); + + it('emits deleteNote when first note emits handleDeleteNote', () => { + note.vm.$emit('handleDeleteNote'); + expect(wrapper.emitted().deleteNote).toBeTruthy(); + }); + }); + }); + describe('componentData', () => { beforeEach(() => { createComponent(); diff --git a/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js b/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js index e43d5301a50..b85e2673624 100644 --- a/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js +++ b/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js @@ -88,7 +88,7 @@ describe('RelatedIssuableItem', () => { }); it('renders state title', () => { - const stateTitle = tokenState.attributes('data-original-title'); + const stateTitle = tokenState.attributes('title'); const formatedCreateDate = formatDate(props.createdAt); expect(stateTitle).toContain('<span class="bold">Opened</span>'); @@ -155,7 +155,9 @@ describe('RelatedIssuableItem', () => { describe('token assignees', () => { it('renders assignees avatars', () => { - expect(wrapper.findAll('.item-assignees .user-avatar-link').length).toBe(2); + // Expect 2 times 2 because assignees are rendered twice, due to layout issues + expect(wrapper.findAll('.item-assignees .user-avatar-link').length).toBeDefined(); + expect(wrapper.find('.item-assignees .avatar-counter').text()).toContain('+2'); }); }); diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js index ac2fb16bd10..30e0504e4e1 100644 --- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js @@ -473,7 +473,7 @@ describe('mrWidgetOptions', () => { vm.mr.relatedLinks = { assignToMe: null, closing: ` - <a class="close-related-link" href="#'> + <a class="close-related-link" href="#"> Close </a> `, diff --git a/spec/lib/gitlab/metrics/dashboard/dynamic_dashboard_service_spec.rb b/spec/lib/gitlab/metrics/dashboard/dynamic_dashboard_service_spec.rb index eecd257b38d..79a78df44ae 100644 --- a/spec/lib/gitlab/metrics/dashboard/dynamic_dashboard_service_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/dynamic_dashboard_service_spec.rb @@ -6,13 +6,19 @@ describe Gitlab::Metrics::Dashboard::DynamicDashboardService, :use_clean_rails_m include MetricsDashboardHelpers set(:project) { build(:project) } + set(:user) { create(:user) } set(:environment) { create(:environment, project: project) } + before do + project.add_maintainer(user) + end + describe '#get_dashboard' do - let(:service_params) { [project, nil, { environment: environment, dashboard_path: nil }] } + let(:service_params) { [project, user, { environment: environment, dashboard_path: nil }] } let(:service_call) { described_class.new(*service_params).get_dashboard } it_behaves_like 'valid embedded dashboard service response' + it_behaves_like 'raises error for users with insufficient permissions' it 'caches the unprocessed dashboard for subsequent calls' do expect(YAML).to receive(:safe_load).once.and_call_original diff --git a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb index b9a5ee9c2b3..d8ed54c0248 100644 --- a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb @@ -6,12 +6,17 @@ describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_cachi include MetricsDashboardHelpers set(:project) { build(:project) } + set(:user) { create(:user) } set(:environment) { create(:environment, project: project) } let(:system_dashboard_path) { Gitlab::Metrics::Dashboard::SystemDashboardService::SYSTEM_DASHBOARD_PATH} + before do + project.add_maintainer(user) + end + describe '.find' do let(:dashboard_path) { '.gitlab/dashboards/test.yml' } - let(:service_call) { described_class.find(project, nil, environment, dashboard_path: dashboard_path) } + let(:service_call) { described_class.find(project, user, environment, dashboard_path: dashboard_path) } it_behaves_like 'misconfigured dashboard service response', :not_found @@ -41,13 +46,13 @@ describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_cachi end context 'when no dashboard is specified' do - let(:service_call) { described_class.find(project, nil, environment) } + let(:service_call) { described_class.find(project, user, environment) } it_behaves_like 'valid dashboard service response' end context 'when the dashboard is expected to be embedded' do - let(:service_call) { described_class.find(project, nil, environment, dashboard_path: nil, embedded: true) } + let(:service_call) { described_class.find(project, user, environment, dashboard_path: nil, embedded: true) } it_behaves_like 'valid embedded dashboard service response' end diff --git a/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb b/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb index 57d82421b5d..468e8ec9ef2 100644 --- a/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb @@ -5,8 +5,8 @@ require 'rails_helper' describe Gitlab::Metrics::Dashboard::ProjectDashboardService, :use_clean_rails_memory_store_caching do include MetricsDashboardHelpers - set(:user) { build(:user) } - set(:project) { build(:project) } + set(:user) { create(:user) } + set(:project) { create(:project) } set(:environment) { create(:environment, project: project) } before do @@ -22,6 +22,8 @@ describe Gitlab::Metrics::Dashboard::ProjectDashboardService, :use_clean_rails_m it_behaves_like 'misconfigured dashboard service response', :not_found end + it_behaves_like 'raises error for users with insufficient permissions' + context 'when the dashboard exists' do let(:project) { project_with_dashboard(dashboard_path) } diff --git a/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb b/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb index 2af745bd4d7..13f22dd01c5 100644 --- a/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb @@ -5,15 +5,21 @@ require 'spec_helper' describe Gitlab::Metrics::Dashboard::SystemDashboardService, :use_clean_rails_memory_store_caching do include MetricsDashboardHelpers - set(:project) { build(:project) } + set(:user) { create(:user) } + set(:project) { create(:project) } set(:environment) { create(:environment, project: project) } + before do + project.add_maintainer(user) + end + describe 'get_dashboard' do let(:dashboard_path) { described_class::SYSTEM_DASHBOARD_PATH } - let(:service_params) { [project, nil, { environment: environment, dashboard_path: dashboard_path }] } + let(:service_params) { [project, user, { environment: environment, dashboard_path: dashboard_path }] } let(:service_call) { described_class.new(*service_params).get_dashboard } it_behaves_like 'valid dashboard service response' + it_behaves_like 'raises error for users with insufficient permissions' it 'caches the unprocessed dashboard for subsequent calls' do expect(YAML).to receive(:safe_load).once.and_call_original diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb index 05b3035e591..471769e4aab 100644 --- a/spec/models/clusters/platforms/kubernetes_spec.rb +++ b/spec/models/clusters/platforms/kubernetes_spec.rb @@ -2,13 +2,11 @@ require 'spec_helper' -describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching do +describe Clusters::Platforms::Kubernetes do include KubernetesHelpers - include ReactiveCachingHelpers it { is_expected.to belong_to(:cluster) } it { is_expected.to be_kind_of(Gitlab::Kubernetes) } - it { is_expected.to be_kind_of(ReactiveCaching) } it { is_expected.to respond_to :ca_pem } it { is_expected.to validate_exclusion_of(:namespace).in_array(%w(gitlab-managed-apps)) } @@ -397,17 +395,16 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching end describe '#terminals' do - subject { service.terminals(environment) } + subject { service.terminals(environment, pods: pods) } let!(:cluster) { create(:cluster, :project, platform_kubernetes: service) } let(:project) { cluster.project } let(:service) { create(:cluster_platform_kubernetes, :configured) } let(:environment) { build(:environment, project: project, name: "env", slug: "env-000000") } + let(:pods) { [{ "bad" => "pod" }] } context 'with invalid pods' do it 'returns no terminals' do - stub_reactive_cache(service, pods: [{ "bad" => "pod" }]) - is_expected.to be_empty end end @@ -416,13 +413,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching let(:pod) { kube_pod(environment_slug: environment.slug, namespace: cluster.kubernetes_namespace_for(project), project_slug: project.full_path_slug) } let(:pod_with_no_terminal) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug, status: "Pending") } let(:terminals) { kube_terminals(service, pod) } - - before do - stub_reactive_cache( - service, - pods: [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")] - ) - end + let(:pods) { [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")] } it 'returns terminals' do is_expected.to eq(terminals + terminals) @@ -437,16 +428,18 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching end end - describe '#calculate_reactive_cache' do - subject { service.calculate_reactive_cache } - - let!(:cluster) { create(:cluster, :project, enabled: enabled, platform_kubernetes: service) } + describe '#calculate_reactive_cache_for' do let(:service) { create(:cluster_platform_kubernetes, :configured) } - let(:enabled) { true } - let(:namespace) { cluster.kubernetes_namespace_for(cluster.project) } + let(:pod) { kube_pod } + let(:namespace) { pod["metadata"]["namespace"] } + let(:environment) { instance_double(Environment, deployment_namespace: namespace) } - context 'when cluster is disabled' do - let(:enabled) { false } + subject { service.calculate_reactive_cache_for(environment) } + + context 'when the kubernetes integration is disabled' do + before do + allow(service).to receive(:enabled?).and_return(false) + end it { is_expected.to be_nil } end @@ -457,7 +450,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching stub_kubeclient_deployments(namespace) end - it { is_expected.to include(pods: [kube_pod]) } + it { is_expected.to include(pods: [pod]) } end context 'when kubernetes responds with 500s' do @@ -477,11 +470,5 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching it { is_expected.to include(pods: []) } end - - context 'when the cluster is not project level' do - let(:cluster) { create(:cluster, :group, platform_kubernetes: service) } - - it { is_expected.to include(pods: []) } - end end end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 379dda1f5c4..fe4d64818b4 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -2,10 +2,14 @@ require 'spec_helper' -describe Environment do +describe Environment, :use_clean_rails_memory_store_caching do + include ReactiveCachingHelpers + let(:project) { create(:project, :stubbed_repository) } subject(:environment) { create(:environment, project: project) } + it { is_expected.to be_kind_of(ReactiveCaching) } + it { is_expected.to belong_to(:project).required } it { is_expected.to have_many(:deployments) } @@ -573,32 +577,65 @@ describe Environment do describe '#terminals' do subject { environment.terminals } - context 'when the environment has terminals' do + before do + allow(environment).to receive(:deployment_platform).and_return(double) + end + + context 'reactive cache is empty' do before do - allow(environment).to receive(:has_terminals?).and_return(true) + stub_reactive_cache(environment, nil) end - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } + it { is_expected.to be_nil } + end + + context 'reactive cache has pod data' do + let(:cache_data) { Hash(pods: %w(pod1 pod2)) } + + before do + stub_reactive_cache(environment, cache_data) + end - it 'returns the terminals from the deployment service' do - expect(environment.deployment_platform) - .to receive(:terminals).with(environment) - .and_return(:fake_terminals) + it 'retrieves terminals from the deployment platform' do + expect(environment.deployment_platform) + .to receive(:terminals).with(environment, cache_data) + .and_return(:fake_terminals) - is_expected.to eq(:fake_terminals) - end + is_expected.to eq(:fake_terminals) end end + end + + describe '#calculate_reactive_cache' do + let(:cluster) { create(:cluster, :project, :provided_by_user) } + let(:project) { cluster.project } + let(:environment) { create(:environment, project: project) } + let!(:deployment) { create(:deployment, :success, environment: environment) } + + subject { environment.calculate_reactive_cache } + + it 'returns cache data from the deployment platform' do + expect(environment.deployment_platform).to receive(:calculate_reactive_cache_for) + .with(environment).and_return(pods: %w(pod1 pod2)) + + is_expected.to eq(pods: %w(pod1 pod2)) + end - context 'when the environment does not have terminals' do + context 'environment does not have terminals available' do before do allow(environment).to receive(:has_terminals?).and_return(false) end it { is_expected.to be_nil } end + + context 'project is pending deletion' do + before do + allow(environment.project).to receive(:pending_delete?).and_return(true) + end + + it { is_expected.to be_nil } + end end describe '#has_metrics?' do diff --git a/spec/models/postgresql/replication_slot_spec.rb b/spec/models/postgresql/replication_slot_spec.rb index e100af7ddc7..95ae204a8a8 100644 --- a/spec/models/postgresql/replication_slot_spec.rb +++ b/spec/models/postgresql/replication_slot_spec.rb @@ -47,5 +47,13 @@ describe Postgresql::ReplicationSlot, :postgresql do expect(described_class.lag_too_great?).to eq(false) end + + it 'returns false when there is a nil replication lag' do + expect(described_class) + .to receive(:pluck) + .and_return([0.megabytes, nil]) + + expect(described_class.lag_too_great?).to eq(false) + end end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index c5ab7e57272..0bd17dbacd7 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -844,6 +844,19 @@ describe Repository do end end + describe '#get_raw_changes' do + context `with non-UTF8 bytes in paths` do + let(:old_rev) { 'd0888d297eadcd7a345427915c309413b1231e65' } + let(:new_rev) { '19950f03c765f7ac8723a73a0599764095f52fc0' } + let(:changes) { repository.raw_changes_between(old_rev, new_rev) } + + it 'returns the changes' do + expect { changes }.not_to raise_error + expect(changes.first.new_path.bytes).to eq("hello\x80world".bytes) + end + end + end + describe '#create_ref' do it 'redirects the call to write_ref' do ref, ref_path = '1', '2' diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index a170bb14144..ff4228c9b99 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -# user GET /u/:username/ -# user_groups GET /u/:username/groups(.:format) -# user_projects GET /u/:username/projects(.:format) -# user_contributed_projects GET /u/:username/contributed(.:format) -# user_snippets GET /u/:username/snippets(.:format) -# user_calendar GET /u/:username/calendar(.:format) -# user_calendar_activities GET /u/:username/calendar_activities(.:format) +# user GET /users/:username/ +# user_groups GET /users/:username/groups(.:format) +# user_projects GET /users/:username/projects(.:format) +# user_contributed_projects GET /users/:username/contributed(.:format) +# user_snippets GET /users/:username/snippets(.:format) +# user_calendar GET /users/:username/calendar(.:format) +# user_calendar_activities GET /users/:username/calendar_activities(.:format) describe UsersController, "routing" do it "to #show" do allow_any_instance_of(::Constraints::UserUrlConstrainer).to receive(:matches?).and_return(true) @@ -37,22 +37,6 @@ describe UsersController, "routing" do it "to #calendar_activities" do expect(get("/users/User/calendar_activities")).to route_to('users#calendar_activities', username: 'User') end - - describe 'redirect alias routes' do - include RSpec::Rails::RequestExampleGroup - - it '/u/user1 redirects to /user1' do - expect(get("/u/user1")).to redirect_to('/user1') - end - - it '/u/user1/groups redirects to /user1/groups' do - expect(get("/u/user1/groups")).to redirect_to('/users/user1/groups') - end - - it '/u/user1/projects redirects to /user1/projects' do - expect(get("/u/user1/projects")).to redirect_to('/users/user1/projects') - end - end end # search GET /search(.:format) search#show diff --git a/spec/support/helpers/jira_service_helper.rb b/spec/support/helpers/jira_service_helper.rb index f4d5343c4ed..477bbf1c2e0 100644 --- a/spec/support/helpers/jira_service_helper.rb +++ b/spec/support/helpers/jira_service_helper.rb @@ -25,7 +25,7 @@ module JiraServiceHelper \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"}, \"displayName\":\"GitLab\",\"active\":true}, - \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned JIRA-1 in Merge request of [gitlab-org/gitlab-test|http://localhost:3000/gitlab-org/gitlab-test/merge_requests/2].\", + \"body\":\"[Administrator|http://localhost:3000/root] mentioned JIRA-1 in Merge request of [gitlab-org/gitlab-test|http://localhost:3000/gitlab-org/gitlab-test/merge_requests/2].\", \"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", @@ -40,7 +40,7 @@ module JiraServiceHelper \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true}, - \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned this issue in [a commit of h5bp/html5-boilerplate|http://localhost:3000/h5bp/html5-boilerplate/commit/2439f77897122fbeee3bfd9bb692d3608848433e].\", + \"body\":\"[Administrator|http://localhost:3000/root] mentioned this issue in [a commit of h5bp/html5-boilerplate|http://localhost:3000/h5bp/html5-boilerplate/commit/2439f77897122fbeee3bfd9bb692d3608848433e].\", \"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", diff --git a/spec/support/helpers/metrics_dashboard_helpers.rb b/spec/support/helpers/metrics_dashboard_helpers.rb index 6de00eea474..1511a2f6b49 100644 --- a/spec/support/helpers/metrics_dashboard_helpers.rb +++ b/spec/support/helpers/metrics_dashboard_helpers.rb @@ -50,4 +50,12 @@ module MetricsDashboardHelpers it_behaves_like 'valid dashboard service response for schema' end + + shared_examples_for 'raises error for users with insufficient permissions' do + context 'when the user does not have sufficient access' do + let(:user) { build(:user) } + + it_behaves_like 'misconfigured dashboard service response', :unauthorized + end + end end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 77f22d9dd24..e63099d89b7 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -64,7 +64,8 @@ module TestEnv 'with-codeowners' => '219560e', 'submodule_inside_folder' => 'b491b92', 'png-lfs' => 'fe42f41', - 'sha-starting-with-large-number' => '8426165' + 'sha-starting-with-large-number' => '8426165', + 'invalid-utf8-diff-paths' => '99e4853' }.freeze # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb index c6c10001bc5..2befbcb3370 100644 --- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb @@ -17,7 +17,7 @@ describe 'layouts/nav/sidebar/_project' do it 'has board tab' do render - expect(rendered).to have_css('a[title="Board"]') + expect(rendered).to have_css('a[title="Boards"]') end end diff --git a/spec/workers/reactive_caching_worker_spec.rb b/spec/workers/reactive_caching_worker_spec.rb index b8ca6063ccd..ca0e76fc19a 100644 --- a/spec/workers/reactive_caching_worker_spec.rb +++ b/spec/workers/reactive_caching_worker_spec.rb @@ -3,17 +3,16 @@ require 'spec_helper' describe ReactiveCachingWorker do - let(:service) { project.deployment_platform } - describe '#perform' do context 'when user configured kubernetes from CI/CD > Clusters' do let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } let(:project) { cluster.project } + let!(:environment) { create(:environment, project: project) } it 'calls #exclusively_update_reactive_cache!' do - expect_any_instance_of(Clusters::Platforms::Kubernetes).to receive(:exclusively_update_reactive_cache!) + expect_any_instance_of(Environment).to receive(:exclusively_update_reactive_cache!) - described_class.new.perform("Clusters::Platforms::Kubernetes", service.id) + described_class.new.perform("Environment", environment.id) end end end diff --git a/yarn.lock b/yarn.lock index bbab157d4c7..1f1b35eed0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2231,6 +2231,14 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" integrity sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw== +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -2460,6 +2468,13 @@ classlist-polyfill@^1.2.0: resolved "https://registry.yarnpkg.com/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz#935bc2dfd9458a876b279617514638bcaa964a2e" integrity sha1-k1vC39lFiodrJ5YXUUY4vKqWSi4= +clean-css@^4.1.6, clean-css@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" + integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g== + dependencies: + source-map "~0.6.0" + cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" @@ -2953,6 +2968,11 @@ crypto-random-string@^1.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= +css-b64-images@~0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/css-b64-images/-/css-b64-images-0.2.5.tgz#42005d83204b2b4a5d93b6b1a5644133b5927a02" + integrity sha1-QgBdgyBLK0pdk7axpWRBM7WSegI= + css-loader@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.1.tgz#6885bb5233b35ec47b006057da01cc640b6b79fe" @@ -5370,6 +5390,19 @@ html-entities@^1.2.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2" integrity sha1-QZSMr4XOgv7Tbk5qDtNxpmZDeeI= +html-minifier@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-4.0.0.tgz#cca9aad8bce1175e02e17a8c33e46d8988889f56" + integrity sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig== + dependencies: + camel-case "^3.0.0" + clean-css "^4.2.1" + commander "^2.19.0" + he "^1.2.0" + param-case "^2.1.1" + relateurl "^0.2.7" + uglify-js "^3.5.1" + html-tags@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" @@ -7141,6 +7174,11 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + lowercase-keys@1.0.0, lowercase-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" @@ -7384,10 +7422,10 @@ merge2@^1.2.3: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5" integrity sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA== -mermaid@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.0.0.tgz#8f6c75017e788a8c3997e20c5e5046c2b88d1a8f" - integrity sha512-vUQRykev0A6RtxIVqQT3a9TDxcSbdZbQF5JDyKgidnYuJy8BE8jp6LM+HKDSQuroKm6buu4NlpMO+qhxIP/cTg== +mermaid@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.1.0.tgz#f9f4c02cf98d2d9fae230d5ce28f531e605e9b72" + integrity sha512-fsCN8bOukYHZT6FlA0eIeLs/O3H2+CWcHnxRrS86Ci1cpJes5/qvoye0xjhe8lbXJCFLM8sXWVg57aMHPtnAaw== dependencies: d3 "^5.7.0" dagre-d3-renderer "^0.5.8" @@ -7395,7 +7433,8 @@ mermaid@^8.0.0: graphlibrary "^2.2.0" he "^1.2.0" lodash "^4.17.11" - moment "^2.23.0" + minify "^4.1.1" + moment-mini "^2.22.1" scope-css "^1.2.1" methods@~1.1.2: @@ -7467,6 +7506,19 @@ mimic-response@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" integrity sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4= +minify@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/minify/-/minify-4.1.2.tgz#88755f4faa5f7ab6d0c64fdd659aa34ea658f180" + integrity sha512-YY6b6VzV7AY2MTMt1GjoFqKthGWvAr2L7MrzmFyiEsvPX+XAvidHcKqu36LlDT1V4I80ncbV5bsdTnIJq4/Sdw== + dependencies: + clean-css "^4.1.6" + css-b64-images "~0.2.5" + debug "^4.1.0" + html-minifier "^4.0.0" + terser "^4.0.0" + try-catch "^2.0.0" + try-to-catch "^1.0.2" + minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" @@ -7553,7 +7605,12 @@ mkdirp@0.5.x, mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp dependencies: minimist "0.0.8" -moment@2.x, moment@^2.10.2, moment@^2.23.0: +moment-mini@^2.22.1: + version "2.22.1" + resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.22.1.tgz#bc32d73e43a4505070be6b53494b17623183420d" + integrity sha512-OUCkHOz7ehtNMYuZjNciXUfwTuz8vmF1MTbAy59ebf+ZBYZO5/tZKuChVWCX+uDo+4idJBpGltNfV8st+HwsGw== + +moment@2.x, moment@^2.10.2: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== @@ -7666,6 +7723,13 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + node-ensure@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/node-ensure/-/node-ensure-0.0.0.tgz#ecae764150de99861ec5c810fd5d096b183932a7" @@ -8259,6 +8323,13 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" +param-case@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + parse-asn1@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" @@ -9305,6 +9376,11 @@ regjsparser@^0.6.0: dependencies: jsesc "~0.5.0" +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + remark-parse@^6.0.0: version "6.0.3" resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-6.0.3.tgz#c99131052809da482108413f87b0ee7f52180a3a" @@ -10027,7 +10103,7 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.6, source-map-support@~0.5.6: +source-map-support@^0.5.6, source-map-support@~0.5.10, source-map-support@~0.5.6: version "0.5.12" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== @@ -10057,7 +10133,7 @@ source-map@^0.5.0, source-map@^0.5.6: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -10588,6 +10664,15 @@ terser@^3.8.1: source-map "~0.6.1" source-map-support "~0.5.6" +terser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.0.0.tgz#ef356f6f359a963e2cc675517f21c1c382877374" + integrity sha512-dOapGTU0hETFl1tCo4t56FN+2jffoKyER9qBGoUFyZ6y7WLoKT0bF+lAYi6B6YsILcGF3q1C2FBh8QcKSCgkgA== + dependencies: + commander "^2.19.0" + source-map "~0.6.1" + source-map-support "~0.5.10" + test-exclude@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.0.0.tgz#cdce7cece785e0e829cd5c2b27baf18bc583cfb7" @@ -10842,6 +10927,16 @@ trough@^1.0.0: dependencies: glob "^7.1.2" +try-catch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/try-catch/-/try-catch-2.0.0.tgz#a491141d597f8b72b46757fe1c47059341a16aed" + integrity sha512-RPXpVjsbtWgymwGq5F/OWDFsjEzdvzwHFaMjWWW6f/p6+uk/N7YSKJHQfIfGqITfj8qH4cBqCLMnhKZBaKk7Kg== + +try-to-catch@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/try-to-catch/-/try-to-catch-1.1.1.tgz#770162dd13b9a0e55da04db5b7f888956072038a" + integrity sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA== + tryer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7" @@ -10928,10 +11023,10 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg== -uglify-js@^3.1.4: - version "3.5.15" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.5.15.tgz#fe2b5378fd0b09e116864041437bff889105ce24" - integrity sha512-fe7aYFotptIddkwcm6YuA0HmknBZ52ZzOsUxZEdhhkSsz7RfjHDX2QDxwKTiv4JQ5t5NhfmpgAK+J7LiDhKSqg== +uglify-js@^3.1.4, uglify-js@^3.5.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5" + integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg== dependencies: commander "~2.20.0" source-map "~0.6.1" @@ -11128,6 +11223,11 @@ update-notifier@^2.5.0: semver-diff "^2.0.0" xdg-basedir "^3.0.0" +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" |