diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-16 15:09:33 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-16 15:09:33 +0300 |
commit | 3775eba7c1d41443461e3abcdab2867bbc4636ae (patch) | |
tree | 4c1ed52aa0dd296c1608e2d1d6911e86cdf29abf /app | |
parent | ed7568cc8083a9f8923d1a26bc0d8f60e3f629a3 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
36 files changed, 322 insertions, 283 deletions
diff --git a/app/assets/javascripts/confidential_merge_request/components/dropdown.vue b/app/assets/javascripts/confidential_merge_request/components/dropdown.vue index 5b4bdca46e4..6bb654a434f 100644 --- a/app/assets/javascripts/confidential_merge_request/components/dropdown.vue +++ b/app/assets/javascripts/confidential_merge_request/components/dropdown.vue @@ -1,12 +1,11 @@ <script> -import { GlDeprecatedDropdown, GlDeprecatedDropdownItem, GlIcon } from '@gitlab/ui'; +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { __ } from '~/locale'; export default { components: { - GlDeprecatedDropdown, - GlDeprecatedDropdownItem, - GlIcon, + GlDropdown, + GlDropdownItem, }, props: { projects: { @@ -37,25 +36,15 @@ export default { </script> <template> - <gl-deprecated-dropdown toggle-class="d-flex align-items-center w-100" class="w-100"> - <template #button-content> - <span class="str-truncated-100 mr-2"> - <gl-icon name="lock" /> - {{ dropdownText }} - </span> - <gl-icon name="chevron-down" class="ml-auto" /> - </template> - <gl-deprecated-dropdown-item + <gl-dropdown block icon="lock" :text="dropdownText"> + <gl-dropdown-item v-for="project in projects" :key="project.id" + :is-check-item="true" + :is-checked="project.id === selectedProject.id" @click="selectProject(project)" > - <gl-icon - name="mobile-issue-close" - :class="{ icon: project.id !== selectedProject.id }" - class="js-active-project-check" - /> - <span class="ml-1">{{ project.name }}</span> - </gl-deprecated-dropdown-item> - </gl-deprecated-dropdown> + {{ project.name }} + </gl-dropdown-item> + </gl-dropdown> </template> diff --git a/app/assets/javascripts/design_management/pages/design/index.vue b/app/assets/javascripts/design_management/pages/design/index.vue index 705bc6fa528..6a96b06dcd8 100644 --- a/app/assets/javascripts/design_management/pages/design/index.vue +++ b/app/assets/javascripts/design_management/pages/design/index.vue @@ -307,11 +307,13 @@ export default { <template> <div - class="design-detail js-design-detail fixed-top w-100 position-bottom-0 d-flex justify-content-center flex-column flex-lg-row" + class="design-detail js-design-detail fixed-top gl-w-full gl-bottom-0 gl-display-flex gl-justify-content-center gl-flex-direction-column gl-lg-flex-direction-row" > - <gl-loading-icon v-if="isFirstLoading" size="xl" class="align-self-center" /> + <gl-loading-icon v-if="isFirstLoading" size="xl" class="gl-align-self-center" /> <template v-else> - <div class="d-flex overflow-hidden flex-grow-1 flex-column position-relative"> + <div + class="gl-display-flex gl-overflow-hidden gl-flex-grow-1 gl-flex-direction-column gl-relative" + > <design-destroyer :filenames="[design.filename]" :project-path="projectPath" @@ -330,7 +332,7 @@ export default { </template> </design-destroyer> - <div v-if="errorMessage" class="p-3"> + <div v-if="errorMessage" class="gl-p-5"> <gl-alert variant="danger" @dismiss="errorMessage = null"> {{ errorMessage }} </gl-alert> @@ -347,7 +349,9 @@ export default { @moveNote="onMoveNote" /> - <div class="design-scaler-wrapper position-absolute mb-4 d-flex-center"> + <div + class="design-scaler-wrapper gl-absolute gl-mb-6 gl-display-flex gl-justify-content-center gl-align-items-center" + > <design-scaler @scale="scale = $event" /> </div> </div> diff --git a/app/assets/javascripts/design_management/pages/index.vue b/app/assets/javascripts/design_management/pages/index.vue index eab46d146da..6e71dca41e9 100644 --- a/app/assets/javascripts/design_management/pages/index.vue +++ b/app/assets/javascripts/design_management/pages/index.vue @@ -335,7 +335,7 @@ export default { @mouseenter="toggleOnPasteListener" @mouseleave="toggleOffPasteListener" > - <header v-if="showToolbar" class="row-content-block border-top-0 p-2 d-flex"> + <header v-if="showToolbar" class="row-content-block gl-border-t-0 gl-p-3 gl-display-flex"> <div class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-w-full"> <div> <span class="gl-font-weight-bold gl-mr-3">{{ s__('DesignManagement|Designs') }}</span> @@ -375,7 +375,7 @@ export default { </div> </div> </header> - <div class="mt-4"> + <div class="gl-mt-6"> <gl-loading-icon v-if="isLoading" size="md" /> <gl-alert v-else-if="error" variant="danger" :dismissible="false"> {{ __('An error occurred while loading designs. Please try again.') }} @@ -385,7 +385,7 @@ export default { class="card" data-testid="design-collection-is-copying" > - <div class="card-header design-card-header border-bottom-0"> + <div class="card-header design-card-header gl-border-b-0"> <div class="card-title gl-display-flex gl-align-items-center gl-my-0 gl-h-7"> {{ s__( diff --git a/app/assets/javascripts/emoji/index.js b/app/assets/javascripts/emoji/index.js index c06ecb3a8d9..4a56843c0b5 100644 --- a/app/assets/javascripts/emoji/index.js +++ b/app/assets/javascripts/emoji/index.js @@ -82,6 +82,12 @@ export function getAllEmoji() { * @returns {Object} The matching emoji. */ export function getEmoji(query, fallback = false) { + // TODO https://gitlab.com/gitlab-org/gitlab/-/issues/268208 + const fallbackEmoji = emojiMap.grey_question; + if (!query) { + return fallback ? fallbackEmoji : null; + } + if (!emojiMap) { // eslint-disable-next-line @gitlab/require-i18n-strings throw new Error('The emoji map is uninitialized or initialization has not completed'); @@ -94,11 +100,7 @@ export function getEmoji(query, fallback = false) { return emojiMap[name]; } - if (fallback) { - return emojiMap.grey_question; - } - - return null; + return fallback ? fallbackEmoji : null; } const searchMatchers = { @@ -178,6 +180,15 @@ export function searchEmoji(query, opts) { raw = false, } = opts || {}; + const fallbackEmoji = emojiMap.grey_question; + if (!query) { + if (fallback) { + return raw ? [{ emoji: fallbackEmoji }] : [fallbackEmoji]; + } + + return []; + } + // optimization for an exact match in name and alias if (match === 'exact' && new Set([...fields, 'name', 'alias']).size === 2) { const emoji = getEmoji(query, fallback); @@ -193,16 +204,10 @@ export function searchEmoji(query, opts) { // Fallback to question mark for unknown emojis if (fallback && results.length === 0) { - if (raw) { - return [{ emoji: emojiMap.grey_question }]; - } - return [emojiMap.grey_question]; + return raw ? [{ emoji: fallbackEmoji }] : [fallbackEmoji]; } - if (raw) { - return results; - } - return results.map(r => r.emoji); + return raw ? results : results.map(r => r.emoji); } let emojiCategoryMap; diff --git a/app/assets/javascripts/environments/components/enable_review_app_button.vue b/app/assets/javascripts/environments/components/enable_review_app_button.vue deleted file mode 100644 index 554875b7ce3..00000000000 --- a/app/assets/javascripts/environments/components/enable_review_app_button.vue +++ /dev/null @@ -1,109 +0,0 @@ -<script> -import { GlButton, GlModal, GlModalDirective, GlLink, GlSprintf } from '@gitlab/ui'; -import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; -import { s__ } from '~/locale'; - -export default { - components: { - GlButton, - GlLink, - GlModal, - GlSprintf, - ModalCopyButton, - }, - directives: { - 'gl-modal': GlModalDirective, - }, - instructionText: { - step1: s__( - 'EnableReviewApp|%{stepStart}Step 1%{stepEnd}. Ensure you have Kubernetes set up and have a base domain for your %{linkStart}cluster%{linkEnd}.', - ), - step2: s__('EnableReviewApp|%{stepStart}Step 2%{stepEnd}. Copy the following snippet:'), - step3: s__( - `EnableReviewApp|%{stepStart}Step 3%{stepEnd}. Add it to the project %{linkStart}gitlab-ci.yml%{linkEnd} file.`, - ), - }, - modalInfo: { - closeText: s__('EnableReviewApp|Close'), - copyToClipboardText: s__('EnableReviewApp|Copy snippet text'), - copyString: `deploy_review: - stage: deploy - script: - - echo "Deploy a review app" - environment: - name: review/$CI_COMMIT_REF_NAME - url: https://$CI_ENVIRONMENT_SLUG.example.com - only: - - branches - except: - - master`, - id: 'enable-review-app-info', - title: s__('ReviewApp|Enable Review App'), - }, -}; -</script> -<template> - <div> - <gl-button - v-gl-modal="$options.modalInfo.id" - variant="info" - category="secondary" - type="button" - class="gl-w-full js-enable-review-app-button" - > - {{ s__('Environments|Enable review app') }} - </gl-button> - <gl-modal - :modal-id="$options.modalInfo.id" - :title="$options.modalInfo.title" - size="lg" - class="text-2 ws-normal" - ok-only - ok-variant="light" - :ok-title="$options.modalInfo.closeText" - > - <p> - <gl-sprintf :message="$options.instructionText.step1"> - <template #step="{ content }"> - <strong>{{ content }}</strong> - </template> - <template #link="{ content }"> - <gl-link - href="https://docs.gitlab.com/ee/user/project/clusters/add_remove_clusters.html" - target="_blank" - >{{ content }}</gl-link - > - </template> - </gl-sprintf> - </p> - <div> - <p> - <gl-sprintf :message="$options.instructionText.step2"> - <template #step="{ content }"> - <strong>{{ content }}</strong> - </template> - </gl-sprintf> - </p> - <div class="flex align-items-start"> - <pre class="w-100"> {{ $options.modalInfo.copyString }} </pre> - <modal-copy-button - :title="$options.modalInfo.copyToClipboardText" - :text="$options.modalInfo.copyString" - :modal-id="$options.modalInfo.id" - css-classes="border-0" - /> - </div> - </div> - <p> - <gl-sprintf :message="$options.instructionText.step3"> - <template #step="{ content }"> - <strong>{{ content }}</strong> - </template> - <template #link="{ content }"> - <gl-link href="blob/master/.gitlab-ci.yml" target="_blank">{{ content }}</gl-link> - </template> - </gl-sprintf> - </p> - </gl-modal> - </div> -</template> diff --git a/app/assets/javascripts/environments/components/enable_review_app_modal.vue b/app/assets/javascripts/environments/components/enable_review_app_modal.vue new file mode 100644 index 00000000000..3dd1d5a1bcc --- /dev/null +++ b/app/assets/javascripts/environments/components/enable_review_app_modal.vue @@ -0,0 +1,98 @@ +<script> +import { GlLink, GlModal, GlSprintf } from '@gitlab/ui'; +import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; +import { s__ } from '~/locale'; + +export default { + components: { + GlLink, + GlModal, + GlSprintf, + ModalCopyButton, + }, + props: { + modalId: { + type: String, + required: true, + }, + }, + instructionText: { + step1: s__( + 'EnableReviewApp|%{stepStart}Step 1%{stepEnd}. Ensure you have Kubernetes set up and have a base domain for your %{linkStart}cluster%{linkEnd}.', + ), + step2: s__('EnableReviewApp|%{stepStart}Step 2%{stepEnd}. Copy the following snippet:'), + step3: s__( + `EnableReviewApp|%{stepStart}Step 3%{stepEnd}. Add it to the project %{linkStart}gitlab-ci.yml%{linkEnd} file.`, + ), + }, + modalInfo: { + closeText: s__('EnableReviewApp|Close'), + copyToClipboardText: s__('EnableReviewApp|Copy snippet text'), + copyString: `deploy_review: + stage: deploy + script: + - echo "Deploy a review app" + environment: + name: review/$CI_COMMIT_REF_NAME + url: https://$CI_ENVIRONMENT_SLUG.example.com + only: + - branches + except: + - master`, + title: s__('ReviewApp|Enable Review App'), + }, +}; +</script> +<template> + <gl-modal + :modal-id="modalId" + :title="$options.modalInfo.title" + size="lg" + ok-only + ok-variant="light" + :ok-title="$options.modalInfo.closeText" + > + <p> + <gl-sprintf :message="$options.instructionText.step1"> + <template #step="{ content }"> + <strong>{{ content }}</strong> + </template> + <template #link="{ content }"> + <gl-link + href="https://docs.gitlab.com/ee/user/project/clusters/add_remove_clusters.html" + target="_blank" + >{{ content }}</gl-link + > + </template> + </gl-sprintf> + </p> + <div> + <p> + <gl-sprintf :message="$options.instructionText.step2"> + <template #step="{ content }"> + <strong>{{ content }}</strong> + </template> + </gl-sprintf> + </p> + <div class="gl-display-flex align-items-start"> + <pre class="gl-w-full"> {{ $options.modalInfo.copyString }} </pre> + <modal-copy-button + :title="$options.modalInfo.copyToClipboardText" + :text="$options.modalInfo.copyString" + :modal-id="modalId" + css-classes="border-0" + /> + </div> + </div> + <p> + <gl-sprintf :message="$options.instructionText.step3"> + <template #step="{ content }"> + <strong>{{ content }}</strong> + </template> + <template #link="{ content }"> + <gl-link href="blob/master/.gitlab-ci.yml" target="_blank">{{ content }}</gl-link> + </template> + </gl-sprintf> + </p> + </gl-modal> +</template> diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue index 8c4d374ebe7..9bafc7ed153 100644 --- a/app/assets/javascripts/environments/components/environments_app.vue +++ b/app/assets/javascripts/environments/components/environments_app.vue @@ -1,21 +1,28 @@ <script> -import { GlBadge, GlButton, GlTab, GlTabs } from '@gitlab/ui'; +import { GlBadge, GlButton, GlModalDirective, GlTab, GlTabs } from '@gitlab/ui'; import { deprecatedCreateFlash as Flash } from '~/flash'; import { s__ } from '~/locale'; import emptyState from './empty_state.vue'; import eventHub from '../event_hub'; import environmentsMixin from '../mixins/environments_mixin'; import CIPaginationMixin from '~/vue_shared/mixins/ci_pagination_api_mixin'; -import EnableReviewAppButton from './enable_review_app_button.vue'; +import EnableReviewAppModal from './enable_review_app_modal.vue'; import StopEnvironmentModal from './stop_environment_modal.vue'; import DeleteEnvironmentModal from './delete_environment_modal.vue'; import ConfirmRollbackModal from './confirm_rollback_modal.vue'; export default { + i18n: { + newEnvironmentButtonLabel: s__('Environments|New environment'), + reviewAppButtonLabel: s__('Environments|Enable review app'), + }, + modal: { + id: 'enable-review-app-info', + }, components: { ConfirmRollbackModal, emptyState, - EnableReviewAppButton, + EnableReviewAppModal, GlBadge, GlButton, GlTab, @@ -23,9 +30,10 @@ export default { StopEnvironmentModal, DeleteEnvironmentModal, }, - + directives: { + 'gl-modal': GlModalDirective, + }, mixins: [CIPaginationMixin, environmentsMixin], - props: { endpoint: { type: String, @@ -140,17 +148,25 @@ export default { gl-mt-3 gl-display-md-none!" > - <enable-review-app-button + <gl-button v-if="state.reviewAppDetails.can_setup_review_app" + v-gl-modal="$options.modal.id" + data-testid="enable-review-app" + variant="info" + category="secondary" + type="button" class="gl-mb-3 gl-flex-fill-1" - /> + > + {{ $options.i18n.reviewAppButtonLabel }} + </gl-button> <gl-button v-if="canCreateEnvironment" :href="newEnvironmentPath" + data-testid="new-environment" category="primary" variant="success" > - {{ s__('Environments|New environment') }} + {{ $options.i18n.newEnvironmentButtonLabel }} </gl-button> </div> <gl-tabs content-class="gl-display-none"> @@ -176,17 +192,25 @@ export default { gl-lg-justify-content-end gl-lg-mt-0" > - <enable-review-app-button + <gl-button v-if="state.reviewAppDetails.can_setup_review_app" + v-gl-modal="$options.modal.id" + data-testid="enable-review-app" + variant="info" + category="secondary" + type="button" class="gl-mb-3 gl-lg-mr-3 gl-lg-mb-0" - /> + > + {{ $options.i18n.reviewAppButtonLabel }} + </gl-button> <gl-button v-if="canCreateEnvironment" :href="newEnvironmentPath" + data-testid="new-environment" category="primary" variant="success" > - {{ s__('Environments|New environment') }} + {{ $options.i18n.newEnvironmentButtonLabel }} </gl-button> </div> </template> @@ -208,6 +232,11 @@ export default { <empty-state :help-path="helpPagePath" /> </template> </container> + <enable-review-app-modal + v-if="state.reviewAppDetails.can_setup_review_app" + :modal-id="$options.modal.id" + data-testid="enable-review-app-modal" + /> </div> </div> </template> diff --git a/app/assets/javascripts/packages/details/components/composer_installation.vue b/app/assets/javascripts/packages/details/components/composer_installation.vue index 0518fac98fc..9d87ae8f836 100644 --- a/app/assets/javascripts/packages/details/components/composer_installation.vue +++ b/app/assets/javascripts/packages/details/components/composer_installation.vue @@ -31,34 +31,32 @@ export default { </script> <template> - <div> - <div v-if="groupExists"> - <h3 class="gl-font-lg">{{ __('Installation') }}</h3> + <div v-if="groupExists" data-testid="root-node"> + <h3 class="gl-font-lg">{{ __('Installation') }}</h3> - <code-instruction - :label="$options.i18n.registryInclude" - :instruction="composerRegistryInclude" - :copy-text="$options.i18n.copyRegistryInclude" - :tracking-action="$options.trackingActions.COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND" - :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" - data-testid="registry-include" - /> + <code-instruction + :label="$options.i18n.registryInclude" + :instruction="composerRegistryInclude" + :copy-text="$options.i18n.copyRegistryInclude" + :tracking-action="$options.trackingActions.COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" + data-testid="registry-include" + /> - <code-instruction - :label="$options.i18n.packageInclude" - :instruction="composerPackageInclude" - :copy-text="$options.i18n.copyPackageInclude" - :tracking-action="$options.trackingActions.COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND" - :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" - data-testid="package-include" - /> - <span data-testid="help-text"> - <gl-sprintf :message="$options.i18n.infoLine"> - <template #link="{ content }"> - <gl-link :href="composerHelpPath" target="_blank">{{ content }}</gl-link> - </template> - </gl-sprintf> - </span> - </div> + <code-instruction + :label="$options.i18n.packageInclude" + :instruction="composerPackageInclude" + :copy-text="$options.i18n.copyPackageInclude" + :tracking-action="$options.trackingActions.COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND" + :tracking-label="$options.TrackingLabels.CODE_INSTRUCTION" + data-testid="package-include" + /> + <span data-testid="help-text"> + <gl-sprintf :message="$options.i18n.infoLine"> + <template #link="{ content }"> + <gl-link :href="composerHelpPath" target="_blank">{{ content }}</gl-link> + </template> + </gl-sprintf> + </span> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue b/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue index 133965f0aca..49591a80752 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue @@ -48,7 +48,9 @@ export default { > <ci-icon :status="group.status" /> - <span class="ci-status-text text-truncate mw-70p gl-pl-2 d-inline-block align-bottom"> + <span + class="gl-text-truncate mw-70p gl-pl-2 gl-display-inline-block gl-vertical-align-bottom" + > {{ group.name }} </span> diff --git a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue index 30ba243077e..1b71949784a 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue @@ -27,7 +27,7 @@ export default { <template> <span class="ci-job-name-component mw-100"> <ci-icon :status="status" /> - <span class="ci-status-text text-truncate mw-70p gl-pl-2 d-inline-block align-bottom"> + <span class="gl-text-truncate mw-70p gl-pl-2 gl-display-inline-block gl-vertical-align-bottom"> {{ name }} </span> </span> diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue index cbde2afbd94..3a2b8a20bae 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue @@ -97,6 +97,19 @@ export default { this.reportFailure(DRAW_FAILURE); } }, + getStageBackgroundClass(index) { + const { length } = this.pipelineData.stages; + + if (length === 1) { + return 'stage-rounded'; + } else if (index === 0) { + return 'stage-left-rounded'; + } else if (index === length - 1) { + return 'stage-right-rounded'; + } + + return ''; + }, highlightNeeds(uniqueJobId) { // The first time we hover, we create the object where // we store all the data to properly highlight the needs. @@ -177,10 +190,7 @@ export default { > <div class="gl-display-flex gl-align-items-center gl-bg-white gl-w-full gl-px-8 gl-py-4 gl-mb-5" - :class="{ - 'stage-left-rounded': index === 0, - 'stage-right-rounded': index === pipelineData.stages.length - 1, - }" + :class="getStageBackgroundClass(index)" > <stage-pill :stage-name="stage.name" :is-empty="stage.groups.length === 0" /> </div> diff --git a/app/assets/stylesheets/fontawesome_custom.scss b/app/assets/stylesheets/fontawesome_custom.scss index 7aaf6ff526f..c1d2cb53ed2 100644 --- a/app/assets/stylesheets/fontawesome_custom.scss +++ b/app/assets/stylesheets/fontawesome_custom.scss @@ -185,14 +185,6 @@ content: '\f1d3'; } -.fa-folder::before { - content: '\f07b'; -} - -.fa-archive::before { - content: '\f187'; -} - .fa-thumb-tack::before { content: '\f08d'; } @@ -201,10 +193,6 @@ content: '\f06d'; } -.fa-globe::before { - content: '\f0ac'; -} - .fa-pause::before { content: '\f04c'; } diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 1a394ad124b..5f56fa3be86 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -355,28 +355,45 @@ background-color: $white; } - .boards-switcher { - margin: 0 0 10px; + .filtered-search-block .boards-switcher { + @include gl-mr-0; + margin-bottom: $gl-input-padding; .boards-selector-wrapper, .dropdown { - display: block; + @include gl-display-block; } } .filter-dropdown-container { > div { - margin: 0; + @include gl-m-0; > .btn { - margin: 0 0 10px; - width: 100%; + margin: 0 0 $gl-input-padding; + @include gl-w-full; } } .board-labels-toggle-wrapper { margin-bottom: $gl-input-padding; } + + .board-swimlanes-toggle-wrapper { + @include gl-h-auto; + margin-bottom: $gl-input-padding; + + > span, + > .dropdown, + .gl-dropdown-toggle { + @include gl-w-full; + @include gl-m-0; + } + + > .dropdown { + @include gl-mt-2; + } + } } .boards-add-list > .btn { diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index fe077b4b7e9..9a2e7565882 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -89,11 +89,6 @@ } } -.ci-build-text, -.ci-status-text { - font-weight: 200; -} - /** * Terminal */ @@ -130,6 +125,10 @@ width: 8rem; } +.stage-rounded { + border-radius: 2rem; +} + .stage-left-rounded { border-radius: 2rem 0 0 2rem; } diff --git a/app/graphql/mutations/merge_requests/set_milestone.rb b/app/graphql/mutations/merge_requests/set_milestone.rb index b3412dd9ed2..abcb1bda1f3 100644 --- a/app/graphql/mutations/merge_requests/set_milestone.rb +++ b/app/graphql/mutations/merge_requests/set_milestone.rb @@ -6,7 +6,7 @@ module Mutations graphql_name 'MergeRequestSetMilestone' argument :milestone_id, - GraphQL::ID_TYPE, + ::Types::GlobalIDType[::Milestone], required: false, loads: Types::MilestoneType, description: <<~DESC diff --git a/app/graphql/mutations/notes/base.rb b/app/graphql/mutations/notes/base.rb index d6d5f1e760d..f2678211335 100644 --- a/app/graphql/mutations/notes/base.rb +++ b/app/graphql/mutations/notes/base.rb @@ -16,20 +16,6 @@ module Mutations id = ::Types::GlobalIDType[::Note].coerce_isolated_input(id) GitlabSchema.find_by_gid(id) end - - def check_object_is_noteable!(object) - unless object.is_a?(Noteable) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, - 'Cannot add notes to this resource' - end - end - - def check_object_is_note!(object) - unless object.is_a?(Note) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, - 'Resource is not a note' - end - end end end end diff --git a/app/graphql/mutations/notes/create/base.rb b/app/graphql/mutations/notes/create/base.rb index 530c15f0c10..3cfdaf84760 100644 --- a/app/graphql/mutations/notes/create/base.rb +++ b/app/graphql/mutations/notes/create/base.rb @@ -26,8 +26,6 @@ module Mutations def resolve(args) noteable = authorized_find!(id: args[:noteable_id]) - check_object_is_noteable!(noteable) - note = ::Notes::CreateService.new( noteable.project, current_user, diff --git a/app/graphql/mutations/notes/create/note.rb b/app/graphql/mutations/notes/create/note.rb index 8caf50ab9c6..e97037171f7 100644 --- a/app/graphql/mutations/notes/create/note.rb +++ b/app/graphql/mutations/notes/create/note.rb @@ -7,7 +7,7 @@ module Mutations graphql_name 'CreateNote' argument :discussion_id, - GraphQL::ID_TYPE, + ::Types::GlobalIDType[::Discussion], required: false, description: 'The global id of the discussion this note is in reply to' @@ -17,7 +17,11 @@ module Mutations discussion_id = nil if args[:discussion_id] - discussion = GitlabSchema.object_from_id(args[:discussion_id], expected_type: ::Discussion) + # TODO: remove this line when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + discussion_gid = ::Types::GlobalIDType[::Discussion].coerce_isolated_input(args[:discussion_id]) + discussion = GitlabSchema.find_by_gid(discussion_gid) + authorize_discussion!(discussion) discussion_id = discussion.id diff --git a/app/graphql/mutations/notes/destroy.rb b/app/graphql/mutations/notes/destroy.rb index 83a91f07896..63e5eeb5ecf 100644 --- a/app/graphql/mutations/notes/destroy.rb +++ b/app/graphql/mutations/notes/destroy.rb @@ -15,8 +15,6 @@ module Mutations def resolve(id:) note = authorized_find!(id: id) - check_object_is_note!(note) - ::Notes::DestroyService.new(note.project, current_user).execute(note) { diff --git a/app/graphql/mutations/notes/update/base.rb b/app/graphql/mutations/notes/update/base.rb index 8a2a78a29ec..1d5738ada77 100644 --- a/app/graphql/mutations/notes/update/base.rb +++ b/app/graphql/mutations/notes/update/base.rb @@ -9,7 +9,7 @@ module Mutations authorize :admin_note argument :id, - GraphQL::ID_TYPE, + ::Types::GlobalIDType[::Note], required: true, description: 'The global id of the note to update' diff --git a/app/graphql/mutations/notes/update/image_diff_note.rb b/app/graphql/mutations/notes/update/image_diff_note.rb index 31b8828001b..ef70a8d2bf4 100644 --- a/app/graphql/mutations/notes/update/image_diff_note.rb +++ b/app/graphql/mutations/notes/update/image_diff_note.rb @@ -33,7 +33,7 @@ module Mutations private - def pre_update_checks!(note, args) + def pre_update_checks!(note, _args) unless note.is_a?(DiffNote) && note.position.on_image? raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Resource is not an ImageDiffNote' diff --git a/app/graphql/mutations/notes/update/note.rb b/app/graphql/mutations/notes/update/note.rb index ca97dad6ded..73b9b9bc49a 100644 --- a/app/graphql/mutations/notes/update/note.rb +++ b/app/graphql/mutations/notes/update/note.rb @@ -18,8 +18,8 @@ module Mutations private - def pre_update_checks!(note, _args) - check_object_is_note!(note) + def pre_update_checks!(_note, _args) + # no-op end end end diff --git a/app/graphql/mutations/todos/mark_done.rb b/app/graphql/mutations/todos/mark_done.rb index 748e02d8782..3d73022f266 100644 --- a/app/graphql/mutations/todos/mark_done.rb +++ b/app/graphql/mutations/todos/mark_done.rb @@ -8,7 +8,7 @@ module Mutations authorize :update_todo argument :id, - GraphQL::ID_TYPE, + ::Types::GlobalIDType[::Todo], required: true, description: 'The global id of the todo to mark as done' diff --git a/app/graphql/mutations/todos/restore.rb b/app/graphql/mutations/todos/restore.rb index a0a1772db0a..7c8f92d32f5 100644 --- a/app/graphql/mutations/todos/restore.rb +++ b/app/graphql/mutations/todos/restore.rb @@ -8,7 +8,7 @@ module Mutations authorize :update_todo argument :id, - GraphQL::ID_TYPE, + ::Types::GlobalIDType[::Todo], required: true, description: 'The global id of the todo to restore' diff --git a/app/graphql/resolvers/board_resolver.rb b/app/graphql/resolvers/board_resolver.rb index e42cb427c0c..517f4e514c9 100644 --- a/app/graphql/resolvers/board_resolver.rb +++ b/app/graphql/resolvers/board_resolver.rb @@ -6,7 +6,7 @@ module Resolvers type Types::BoardType, null: true - argument :id, GraphQL::ID_TYPE, + argument :id, ::Types::GlobalIDType[::Board], required: true, description: 'The board\'s ID' diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index aef08c433ab..ae46135e890 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -782,8 +782,6 @@ module ProjectsHelper end def project_access_token_available?(project) - return false if ::Gitlab.com? - can?(current_user, :admin_resource_access_tokens, project) end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 0992e240bf5..4f634386d3c 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -933,7 +933,7 @@ class MergeRequest < ApplicationRecord # rubocop: enable CodeReuse/ServiceClass def diffable_merge_ref? - can_be_merged? && merge_ref_head.present? + merge_ref_head.present? && (Feature.enabled?(:display_merge_conflicts_in_diff, project) || can_be_merged?) end # Returns boolean indicating the merge_status should be rechecked in order to diff --git a/app/models/repository.rb b/app/models/repository.rb index 8142398050d..d4fd202b966 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -860,10 +860,10 @@ class Repository end end - def merge_to_ref(user, source_sha, merge_request, target_ref, message, first_parent_ref) + def merge_to_ref(user, source_sha, merge_request, target_ref, message, first_parent_ref, allow_conflicts = false) branch = merge_request.target_branch - raw.merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref) + raw.merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref, allow_conflicts) end def delete_refs(*ref_names) diff --git a/app/models/u2f_registration.rb b/app/models/u2f_registration.rb index 81415eb383b..1a389081913 100644 --- a/app/models/u2f_registration.rb +++ b/app/models/u2f_registration.rb @@ -4,6 +4,19 @@ class U2fRegistration < ApplicationRecord belongs_to :user + after_commit :schedule_webauthn_migration, on: :create + after_commit :update_webauthn_registration, on: :update, if: :counter_changed? + + def schedule_webauthn_migration + BackgroundMigrationWorker.perform_async('MigrateU2fWebauthn', [id, id]) + end + + def update_webauthn_registration + # When we update the sign count of this registration + # we need to update the sign count of the corresponding webauthn registration + # as well if it exists already + WebauthnRegistration.find_by_credential_xid(webauthn_credential_xid)&.update_attribute(:counter, counter) + end def self.register(user, app_id, params, challenges) u2f = U2F::U2F.new(app_id) @@ -40,4 +53,13 @@ class U2fRegistration < ApplicationRecord rescue JSON::ParserError, NoMethodError, ArgumentError, U2F::Error false end + + private + + def webauthn_credential_xid + # To find the corresponding webauthn registration, we use that + # the key handle of the u2f reg corresponds to the credential xid of the webauthn reg + # (with some base64 back and forth) + Base64.strict_encode64(Base64.urlsafe_decode64(key_handle)) + end end diff --git a/app/services/merge_requests/merge_to_ref_service.rb b/app/services/merge_requests/merge_to_ref_service.rb index 1876b1096fe..c0115e94903 100644 --- a/app/services/merge_requests/merge_to_ref_service.rb +++ b/app/services/merge_requests/merge_to_ref_service.rb @@ -58,8 +58,15 @@ module MergeRequests params[:first_parent_ref] || merge_request.target_branch_ref end + ## + # The parameter `allow_conflicts` is a flag whether merge conflicts should be merged into diff + # Default is false + def allow_conflicts + params[:allow_conflicts] || false + end + def commit - repository.merge_to_ref(current_user, source, merge_request, target_ref, commit_message, first_parent_ref) + repository.merge_to_ref(current_user, source, merge_request, target_ref, commit_message, first_parent_ref, allow_conflicts) rescue Gitlab::Git::PreReceiveError, Gitlab::Git::CommandError => error raise MergeError, error.message end diff --git a/app/services/merge_requests/mergeability_check_service.rb b/app/services/merge_requests/mergeability_check_service.rb index b41a5fa317e..627c747203c 100644 --- a/app/services/merge_requests/mergeability_check_service.rb +++ b/app/services/merge_requests/mergeability_check_service.rb @@ -115,10 +115,14 @@ module MergeRequests def update_merge_status return unless merge_request.recheck_merge_status? + return merge_request.mark_as_unmergeable if merge_request.broken? - if can_git_merge? && merge_to_ref + merge_to_ref_success = merge_to_ref + + update_diff_discussion_positions! if merge_to_ref_success + + if merge_to_ref_success && can_git_merge? merge_request.mark_as_mergeable - update_diff_discussion_positions! else merge_request.mark_as_unmergeable end @@ -149,13 +153,14 @@ module MergeRequests end def can_git_merge? - !merge_request.broken? && repository.can_be_merged?(merge_request.diff_head_sha, merge_request.target_branch) + repository.can_be_merged?(merge_request.diff_head_sha, merge_request.target_branch) end def merge_to_ref return true unless merge_ref_auto_sync_enabled? - result = MergeRequests::MergeToRefService.new(project, merge_request.author).execute(merge_request) + params = { allow_conflicts: Feature.enabled?(:display_merge_conflicts_in_diff, project) } + result = MergeRequests::MergeToRefService.new(project, merge_request.author, params).execute(merge_request) result[:status] == :success end diff --git a/app/services/resource_access_tokens/create_service.rb b/app/services/resource_access_tokens/create_service.rb index 8110db9d191..cdeb57627a8 100644 --- a/app/services/resource_access_tokens/create_service.rb +++ b/app/services/resource_access_tokens/create_service.rb @@ -10,7 +10,6 @@ module ResourceAccessTokens end def execute - return unless feature_enabled? return error("User does not have permission to create #{resource_type} Access Token") unless has_permission_to_create? user = create_user @@ -31,10 +30,6 @@ module ResourceAccessTokens attr_reader :resource_type, :resource - def feature_enabled? - return true unless ::Gitlab.com? - end - def has_permission_to_create? %w(project group).include?(resource_type) && can?(current_user, :admin_resource_access_tokens, resource) end diff --git a/app/services/webauthn/authenticate_service.rb b/app/services/webauthn/authenticate_service.rb index a4513c62c2d..a575a853995 100644 --- a/app/services/webauthn/authenticate_service.rb +++ b/app/services/webauthn/authenticate_service.rb @@ -11,12 +11,6 @@ module Webauthn def execute parsed_device_response = Gitlab::Json.parse(@device_response) - # appid is set for legacy U2F devices, will be used in a future iteration - # rp_id = @app_id - # unless parsed_device_response['clientExtensionResults'] && parsed_device_response['clientExtensionResults']['appid'] - # rp_id = URI(@app_id).host - # end - webauthn_credential = WebAuthn::Credential.from_get(parsed_device_response) encoded_raw_id = Base64.strict_encode64(webauthn_credential.raw_id) stored_webauthn_credential = @user.webauthn_registrations.find_by_credential_xid(encoded_raw_id) @@ -52,10 +46,14 @@ module Webauthn # Verifies that webauthn_credential matches stored_credential with the given challenge # def verify_webauthn_credential(webauthn_credential, stored_credential, challenge, encoder) + # We need to adjust the relaying party id (RP id) we verify against if the registration in question + # is a migrated U2F registration. This is beacuse the appid of U2F and the rp id of WebAuthn differ. + rp_id = webauthn_credential.client_extension_outputs['appid'] ? WebAuthn.configuration.origin : URI(WebAuthn.configuration.origin).host webauthn_credential.response.verify( encoder.decode(challenge), public_key: encoder.decode(stored_credential.public_key), - sign_count: stored_credential.counter) + sign_count: stored_credential.counter, + rp_id: rp_id) end end end diff --git a/app/views/ci/status/_dropdown_graph_badge.html.haml b/app/views/ci/status/_dropdown_graph_badge.html.haml index 4c8bb84c9ef..5e9b02b5fe2 100644 --- a/app/views/ci/status/_dropdown_graph_badge.html.haml +++ b/app/views/ci/status/_dropdown_graph_badge.html.haml @@ -8,12 +8,12 @@ - if status.has_details? = link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item d-flex', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do %span{ class: klass }= sprite_icon(status.icon) - %span.ci-build-text.text-truncate.mw-70p.gl-pl-2= subject.name + %span.gl-text-truncate.mw-70p.gl-pl-2= subject.name - else .menu-item.mini-pipeline-graph-dropdown-item.d-flex{ data: { toggle: 'tooltip', title: tooltip, container: 'body' } } %span{ class: klass }= sprite_icon(status.icon) - %span.ci-build-text.text-truncate.mw-70p.gl-pl-2= subject.name + %span.gl-text-truncate.mw-70p.gl-pl-2= subject.name - if status.has_action? = link_to status.action_path, class: "gl-button ci-action-icon-container ci-action-icon-wrapper js-ci-action-icon", method: status.action_method, data: { toggle: 'tooltip', title: status.action_title, container: 'body' } do diff --git a/app/views/shared/wikis/_form.html.haml b/app/views/shared/wikis/_form.html.haml index 2861ceb2b51..dde1b3afa2d 100644 --- a/app/views/shared/wikis/_form.html.haml +++ b/app/views/shared/wikis/_form.html.haml @@ -17,10 +17,10 @@ = f.hidden_field :last_commit_sha, value: @page.last_commit_sha .form-group.row - .col-sm-12= f.label :title, class: 'control-label-full-width' - .col-sm-12 + .col-sm-2.col-form-label= f.label :title, class: 'control-label-full-width' + .col-sm-10 = f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: s_('Wiki|Page title') - %span.d-inline-block.mw-100.gl-mt-2 + %span.gl-display-inline-block.gl-max-w-full.gl-mt-2.gl-text-gray-600 = sprite_icon('bulb', size: 12, css_class: 'gl-mr-n1') - if @page.persisted? = s_("WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title.") @@ -29,18 +29,18 @@ - else = s_("WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories.") = succeed '.' do - = link_to _('Learn more'), help_page_path('user/project/wiki/index', anchor: 'creating-a-new-wiki-page'), + = link_to _('More information'), help_page_path('user/project/wiki/index', anchor: 'creating-a-new-wiki-page'), target: '_blank', rel: 'noopener noreferrer' .form-group.row - .col-sm-12= f.label :format, class: 'control-label-full-width' - .col-sm-12 + .col-sm-2.col-form-label= f.label :format, class: 'control-label-full-width' + .col-sm-10 .select-wrapper = f.select :format, options_for_select(Wiki::MARKUPS, {selected: @page.format}), {}, class: 'form-control select-control' = icon('chevron-down') .form-group.row - .col-sm-12= f.label :content, class: 'control-label-full-width' - .col-sm-12 + .col-sm-2.col-form-label= f.label :content, class: 'control-label-full-width' + .col-sm-10 = render layout: 'shared/md_preview', locals: { url: wiki_page_path(@wiki, @page, action: :preview_markdown) } do = render 'shared/zen', f: f, attr: :content, classes: 'note-textarea qa-wiki-content-textarea', placeholder: s_("WikiPage|Write your content or drag files hereā¦") = render 'shared/notes/hints' @@ -48,7 +48,7 @@ .clearfix .error-alert - .form-text.text-muted + .form-text.gl-text-gray-600 = succeed '.' do - case @page.format.to_s - when 'rdoc' @@ -65,8 +65,8 @@ = (s_("WikiMarkdownDocs|More examples are in the %{docs_link}") % { docs_link: markdown_link }).html_safe .form-group.row - .col-sm-12= f.label :commit_message, class: 'control-label-full-width' - .col-sm-12= f.text_field :message, class: 'form-control qa-wiki-message-textbox', rows: 18, value: nil + .col-sm-2.col-form-label= f.label :commit_message, class: 'control-label-full-width' + .col-sm-10= f.text_field :message, class: 'form-control qa-wiki-message-textbox', rows: 18, value: nil .form-actions - if @page && @page.persisted? diff --git a/app/views/shared/wikis/edit.html.haml b/app/views/shared/wikis/edit.html.haml index 54816a9aedb..c7d1742da8a 100644 --- a/app/views/shared/wikis/edit.html.haml +++ b/app/views/shared/wikis/edit.html.haml @@ -17,8 +17,6 @@ .nav-controls.pb-md-3.pb-lg-0 - if @page.persisted? - = link_to wiki_page_path(@wiki, @page, action: :history), class: "btn gl-button" do - = s_("Wiki|Page history") - if can?(current_user, :admin_wiki, @wiki.container) #delete-wiki-modal-wrapper{ data: { delete_wiki_url: wiki_page_path(@wiki, @page), page_title: @page.human_title } } |