diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-04 12:08:25 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-04 12:08:25 +0000 |
commit | 7928b47c8e06c1ac3f63d321a73dd527aea4e4c3 (patch) | |
tree | dff8cfaaae2fa981b6d95a866ebd1f7b6b86838c /app | |
parent | 0d8bcdf77d609b3624541de767a0129aa0b7e8d2 (diff) | |
download | gitlab-ce-7928b47c8e06c1ac3f63d321a73dd527aea4e4c3.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
34 files changed, 498 insertions, 178 deletions
diff --git a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue index 76709296c89..cd64579a360 100644 --- a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue +++ b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue @@ -55,7 +55,22 @@ export default { }, computed: { filteredFields() { - return this.showRole ? FIELDS : FIELDS.filter((field) => field.key !== 'role'); + const ignoredFields = []; + + // Show 'action' column only when there are no active tokens or when some of them have a revokePath + const showAction = + this.activeAccessTokens.length === 0 || + this.activeAccessTokens.some((token) => token.revokePath); + + if (!showAction) { + ignoredFields.push('action'); + } + + if (!this.showRole) { + ignoredFields.push('role'); + } + + return FIELDS.filter(({ key }) => !ignoredFields.includes(key)); }, header() { return sprintf(this.$options.i18n.header, { diff --git a/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue b/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue index 3e87088e77e..7d2b9cd3d42 100644 --- a/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue +++ b/app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue @@ -15,11 +15,20 @@ export default { 'Create a new %{codeStart}.gitlab-ci.yml%{codeEnd} file at the root of the repository to get started.', ), btnText: __('Configure pipeline'), + externalCiNote: __("This project's pipeline configuration is located outside this repository"), + externalCiInstructions: __( + 'To edit the pipeline configuration, you must go to the project or external site that hosts the file.', + ), }, inject: { emptyStateIllustrationPath: { default: '', }, + usesExternalConfig: { + default: false, + type: Boolean, + required: false, + }, }, methods: { createEmptyConfigFile() { @@ -33,22 +42,31 @@ export default { <pipeline-editor-file-nav v-on="$listeners" /> <div class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-mt-11"> <img :src="emptyStateIllustrationPath" /> - <h1 class="gl-font-size-h1">{{ $options.i18n.title }}</h1> - <p class="gl-mt-3"> - <gl-sprintf :message="$options.i18n.body"> - <template #code="{ content }"> - <code>{{ content }}</code> - </template> - </gl-sprintf> - </p> - <gl-button - variant="confirm" - class="gl-mt-3" - data-qa-selector="create_new_ci_button" - @click="createEmptyConfigFile" + <div + v-if="usesExternalConfig" + class="gl-display-flex gl-flex-direction-column gl-align-items-center" > - {{ $options.i18n.btnText }} - </gl-button> + <h1 class="gl-font-size-h1">{{ $options.i18n.externalCiNote }}</h1> + <p class="gl-mt-3">{{ $options.i18n.externalCiInstructions }}</p> + </div> + <div v-else class="gl-display-flex gl-flex-direction-column gl-align-items-center"> + <h1 class="gl-font-size-h1">{{ $options.i18n.title }}</h1> + <p class="gl-mt-3"> + <gl-sprintf :message="$options.i18n.body"> + <template #code="{ content }"> + <code>{{ content }}</code> + </template> + </gl-sprintf> + </p> + <gl-button + variant="confirm" + class="gl-mt-3" + data-qa-selector="create_new_ci_button" + @click="createEmptyConfigFile" + > + {{ $options.i18n.btnText }} + </gl-button> + </div> </div> </div> </template> diff --git a/app/assets/javascripts/pipeline_editor/index.js b/app/assets/javascripts/pipeline_editor/index.js index 13dad0b2459..6d91c339833 100644 --- a/app/assets/javascripts/pipeline_editor/index.js +++ b/app/assets/javascripts/pipeline_editor/index.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; +import { parseBoolean } from '~/lib/utils/common_utils'; import { EDITOR_APP_STATUS_LOADING } from './constants'; import { CODE_SNIPPET_SOURCE_SETTINGS } from './components/code_snippet_alert/constants'; import getCurrentBranch from './graphql/queries/client/current_branch.query.graphql'; @@ -42,6 +43,7 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => { projectNamespace, simulatePipelineHelpPagePath, totalBranches, + usesExternalConfig, validateTabIllustrationPath, ymlHelpPagePath, } = el.dataset; @@ -133,6 +135,7 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => { projectNamespace, simulatePipelineHelpPagePath, totalBranches: parseInt(totalBranches, 10), + usesExternalConfig: parseBoolean(usesExternalConfig), validateTabIllustrationPath, ymlHelpPagePath, }, diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue index 548769eb214..ff848a973e3 100644 --- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue +++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue @@ -40,7 +40,7 @@ export default { PipelineEditorHome, PipelineEditorMessages, }, - inject: ['ciConfigPath', 'newMergeRequestPath', 'projectFullPath'], + inject: ['ciConfigPath', 'newMergeRequestPath', 'projectFullPath', 'usesExternalConfig'], data() { return { ciConfigData: {}, @@ -397,7 +397,7 @@ export default { <div class="gl-mt-4 gl-relative" data-qa-selector="pipeline_editor_app"> <gl-loading-icon v-if="isBlobContentLoading" size="lg" class="gl-m-3" /> <pipeline-editor-empty-state - v-else-if="showStartScreen" + v-else-if="showStartScreen || usesExternalConfig" @createEmptyConfigFile="setNewEmptyCiConfigFile" @refetchContent="refetchContent" /> diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js new file mode 100644 index 00000000000..1fd87b9897c --- /dev/null +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js @@ -0,0 +1,35 @@ +import { s__ } from '~/locale'; + +export const I18N = { + manageProtectionsLinkTitle: s__('BranchRules|Manage in Protected Branches'), + targetBranch: s__('BranchRules|Target Branch'), + branchNameOrPattern: s__('BranchRules|Branch name or pattern'), + branch: s__('BranchRules|Target Branch'), + allBranches: s__('BranchRules|All branches'), + protectBranchTitle: s__('BranchRules|Protect branch'), + protectBranchDescription: s__( + 'BranchRules|Keep stable branches secure and force developers to use merge requests. %{linkStart}What are protected branches?%{linkEnd}', + ), + wildcardsHelpText: s__( + 'BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/ are supported', + ), + forcePushTitle: s__('BranchRules|Force push'), + allowForcePushDescription: s__( + 'BranchRules|All users with push access are allowed to force push.', + ), + disallowForcePushDescription: s__('BranchRules|Force push is not allowed.'), + approvalsTitle: s__('BranchRules|Approvals'), + statusChecksTitle: s__('BranchRules|Status checks'), + allowedToPushHeader: s__('BranchRules|Allowed to push (%{total})'), + allowedToMergeHeader: s__('BranchRules|Allowed to merge (%{total})'), + noData: s__('BranchRules|No data to display'), +}; + +export const BRANCH_PARAM_NAME = 'branch'; + +export const ALL_BRANCHES_WILDCARD = '*'; + +export const WILDCARDS_HELP_PATH = + 'user/project/protected_branches#configure-multiple-protected-branches-by-using-a-wildcard'; + +export const PROTECTED_BRANCHES_HELP_PATH = 'user/project/protected_branches'; diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue index 58938b6bf96..6534ff883a6 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue @@ -1,33 +1,172 @@ <script> -import { s__ } from '~/locale'; +import { GlSprintf, GlLink, GlLoadingIcon } from '@gitlab/ui'; +import { sprintf } from '~/locale'; import { getParameterByName } from '~/lib/utils/url_utility'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import branchRulesQuery from '../../queries/branch_rules_details.query.graphql'; +import Protection from './protection.vue'; +import { + I18N, + ALL_BRANCHES_WILDCARD, + BRANCH_PARAM_NAME, + WILDCARDS_HELP_PATH, + PROTECTED_BRANCHES_HELP_PATH, +} from './constants'; + +const wildcardsHelpDocLink = helpPagePath(WILDCARDS_HELP_PATH); +const protectedBranchesHelpDocLink = helpPagePath(PROTECTED_BRANCHES_HELP_PATH); export default { name: 'RuleView', - i18n: { branch: s__('BranchRules|Target Branch') }, - components: {}, - props: { + i18n: I18N, + wildcardsHelpDocLink, + protectedBranchesHelpDocLink, + components: { Protection, GlSprintf, GlLink, GlLoadingIcon }, + inject: { projectPath: { - type: String, - required: true, + default: '', + }, + protectedBranchesPath: { + default: '', + }, + }, + apollo: { + project: { + query: branchRulesQuery, + variables() { + return { + projectPath: this.projectPath, + }; + }, + update({ project: { branchRules } }) { + this.branchProtection = branchRules.nodes.find( + (rule) => rule.name === this.branch, + )?.branchProtection; + }, }, }, data() { return { - branch: getParameterByName('branch'), - protected: getParameterByName('protected'), + branch: getParameterByName(BRANCH_PARAM_NAME), + branchProtection: {}, }; }, + computed: { + forcePushDescription() { + return this.branchProtection?.allowForcePush + ? this.$options.i18n.allowForcePushDescription + : this.$options.i18n.disallowForcePushDescription; + }, + mergeAccessLevels() { + const { mergeAccessLevels } = this.branchProtection || {}; + return this.getAccessLevels(mergeAccessLevels); + }, + pushAccessLevels() { + const { pushAccessLevels } = this.branchProtection || {}; + return this.getAccessLevels(pushAccessLevels); + }, + allowedToMergeHeader() { + return sprintf(this.$options.i18n.allowedToMergeHeader, { + total: this.mergeAccessLevels.total, + }); + }, + allowedToPushHeader() { + return sprintf(this.$options.i18n.allowedToPushHeader, { + total: this.pushAccessLevels.total, + }); + }, + allBranches() { + return this.branch === ALL_BRANCHES_WILDCARD; + }, + allBranchesLabel() { + return this.$options.i18n.allBranches; + }, + branchTitle() { + return this.allBranches + ? this.$options.i18n.targetBranch + : this.$options.i18n.branchNameOrPattern; + }, + }, + methods: { + getAccessLevels(accessLevels = {}) { + const total = accessLevels.edges?.length; + const accessLevelTypes = { total, users: [], groups: [], roles: [] }; + + accessLevels.edges?.forEach(({ node }) => { + if (node.user) { + const src = node.user.avatarUrl; + accessLevelTypes.users.push({ src, ...node.user }); + } else if (node.group) { + accessLevelTypes.groups.push(node); + } else { + accessLevelTypes.roles.push(node); + } + }); + + return accessLevelTypes; + }, + }, }; </script> <template> - <div> - <div class="gl-display-flex gl-flex-direction-column gl-pt-3"> - <strong>{{ $options.i18n.branch }}</strong> - <span class="gl-font-monospace gl-mt-2" data-testid="branch">{{ branch }}</span> + <gl-loading-icon v-if="$apollo.loading" /> + <div v-else-if="!branchProtection">{{ $options.i18n.noData }}</div> + <div v-else> + <strong data-testid="branch-title">{{ branchTitle }}</strong> + <p v-if="!allBranches" class="gl-mb-3 gl-text-gray-400"> + <gl-sprintf :message="$options.i18n.wildcardsHelpText"> + <template #link="{ content }"> + <gl-link :href="$options.wildcardsHelpDocLink"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </p> + + <div v-if="allBranches" class="gl-mt-2" data-testid="branch"> + {{ allBranchesLabel }} </div> + <code v-else class="gl-mt-2" data-testid="branch">{{ branch }}</code> + + <h4 class="gl-mb-1 gl-mt-5">{{ $options.i18n.protectBranchTitle }}</h4> + <gl-sprintf :message="$options.i18n.protectBranchDescription"> + <template #link="{ content }"> + <gl-link :href="$options.protectedBranchesHelpDocLink"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + + <!-- Allowed to push --> + <protection + class="gl-mt-3" + :header="allowedToPushHeader" + :header-link-title="$options.i18n.manageProtectionsLinkTitle" + :header-link-href="protectedBranchesPath" + :roles="pushAccessLevels.roles" + :users="pushAccessLevels.users" + :groups="pushAccessLevels.groups" + /> + + <!-- Force push --> + <strong>{{ $options.i18n.forcePushTitle }}</strong> + <p>{{ forcePushDescription }}</p> + + <!-- Allowed to merge --> + <protection + :header="allowedToMergeHeader" + :header-link-title="$options.i18n.manageProtectionsLinkTitle" + :header-link-href="protectedBranchesPath" + :roles="mergeAccessLevels.roles" + :users="mergeAccessLevels.users" + :groups="mergeAccessLevels.groups" + /> + + <!-- Approvals --> + <!-- Follow-up: add approval section (https://gitlab.com/gitlab-org/gitlab/-/issues/372362) --> - <!-- TODO: List branch rule details (https://gitlab.com/gitlab-org/gitlab/-/issues/372362) --> + <!-- Status checks --> + <!-- Follow-up: add status checks section (https://gitlab.com/gitlab-org/gitlab/-/issues/372362) --> </div> </template> diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection.vue index ec70ab88870..8434b7bfce5 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection.vue @@ -58,7 +58,7 @@ export default { <template #header> <div class="gl-display-flex gl-justify-content-space-between"> <strong>{{ header }}</strong> - <gl-link :href="headerLinkHref" target="_blank">{{ headerLinkTitle }}</gl-link> + <gl-link :href="headerLinkHref">{{ headerLinkTitle }}</gl-link> </div> </template> diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue index 56be0198574..2509c2538b2 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue @@ -45,6 +45,9 @@ export default { this.users.length - this.$options.MAX_VISIBLE_AVATARS, ); }, + commaSeparateList() { + return this.accessLevels.length > 1; + }, }, }; </script> @@ -80,6 +83,7 @@ export default { </gl-avatars-inline> <div v-for="(item, index) in accessLevels" :key="index" data-testid="access-level"> + <span v-if="commaSeparateList && index > 0" data-testid="comma-separator">,</span> {{ item.accessLevelDescription }} </div> </div> diff --git a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js index 10de5f12757..39164063d05 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js +++ b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; -import RuleEdit from './components/edit/index.vue'; +import View from './components/view/index.vue'; export default function mountBranchRules(el) { if (!el) { @@ -14,13 +14,17 @@ export default function mountBranchRules(el) { defaultClient: createDefaultClient(), }); - const { projectPath } = el.dataset; + const { projectPath, protectedBranchesPath } = el.dataset; return new Vue({ el, apolloProvider, + provide: { + projectPath, + protectedBranchesPath, + }, render(h) { - return h(RuleEdit, { props: { projectPath } }); + return h(View); }, }); } diff --git a/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql b/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql new file mode 100644 index 00000000000..3ac165498a1 --- /dev/null +++ b/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql @@ -0,0 +1,50 @@ +query getBranchRulesDetails($projectPath: ID!) { + project(fullPath: $projectPath) { + id + branchRules { + nodes { + name + branchProtection { + allowForcePush + codeOwnerApprovalRequired + mergeAccessLevels { + edges { + node { + accessLevel + accessLevelDescription + group { + id + avatarUrl + } + user { + id + name + avatarUrl + webUrl + } + } + } + } + pushAccessLevels { + edges { + node { + accessLevel + accessLevelDescription + group { + id + avatarUrl + } + user { + id + name + avatarUrl + webUrl + } + } + } + } + } + } + } + } +} diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue index 65e0c87e3f3..94793a535cc 100644 --- a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue +++ b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue @@ -35,10 +35,9 @@ export default { }, }, }, - props: { + inject: { projectPath: { - type: String, - required: true, + default: '', }, }, data() { diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue index 68750318029..2b88f8561d7 100644 --- a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue +++ b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue @@ -1,10 +1,11 @@ <script> -import { GlBadge } from '@gitlab/ui'; +import { GlBadge, GlButton } from '@gitlab/ui'; import { s__ } from '~/locale'; export const i18n = { defaultLabel: s__('BranchRules|default'), protectedLabel: s__('BranchRules|protected'), + detailsButtonLabel: s__('BranchRules|Details'), }; export default { @@ -12,6 +13,12 @@ export default { i18n, components: { GlBadge, + GlButton, + }, + inject: { + branchRulesPath: { + default: '', + }, }, props: { name: { @@ -38,24 +45,30 @@ export default { hasApprovalDetails() { return this.approvalDetails && this.approvalDetails.length; }, + detailsPath() { + return `${this.branchRulesPath}?branch=${this.name}`; + }, }, }; </script> <template> - <div class="gl-border-b gl-pt-5 gl-pb-5"> - <strong class="gl-font-monospace">{{ name }}</strong> + <div class="gl-border-b gl-pt-5 gl-pb-5 gl-display-flex gl-justify-content-space-between"> + <div> + <strong class="gl-font-monospace">{{ name }}</strong> - <gl-badge v-if="isDefault" variant="info" size="sm" class="gl-ml-2">{{ - $options.i18n.defaultLabel - }}</gl-badge> + <gl-badge v-if="isDefault" variant="info" size="sm" class="gl-ml-2">{{ + $options.i18n.defaultLabel + }}</gl-badge> - <gl-badge v-if="isProtected" variant="success" size="sm" class="gl-ml-2">{{ - $options.i18n.protectedLabel - }}</gl-badge> + <gl-badge v-if="isProtected" variant="success" size="sm" class="gl-ml-2">{{ + $options.i18n.protectedLabel + }}</gl-badge> - <ul v-if="hasApprovalDetails" class="gl-pl-6 gl-mt-2 gl-mb-0 gl-text-gray-500"> - <li v-for="(detail, index) in approvalDetails" :key="index">{{ detail }}</li> - </ul> + <ul v-if="hasApprovalDetails" class="gl-pl-6 gl-mt-2 gl-mb-0 gl-text-gray-500"> + <li v-for="(detail, index) in approvalDetails" :key="index">{{ detail }}</li> + </ul> + </div> + <gl-button :href="detailsPath"> {{ $options.i18n.detailsButtonLabel }}</gl-button> </div> </template> diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js b/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js index 35322e2e466..042be089e09 100644 --- a/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js +++ b/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js @@ -12,17 +12,17 @@ const apolloProvider = new VueApollo({ export default function mountBranchRules(el) { if (!el) return null; - const { projectPath } = el.dataset; + const { projectPath, branchRulesPath } = el.dataset; return new Vue({ el, apolloProvider, + provide: { + projectPath, + branchRulesPath, + }, render(createElement) { - return createElement(BranchRulesApp, { - props: { - projectPath, - }, - }); + return createElement(BranchRulesApp); }, }); } diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss index 147d7a92d57..68fe701c7e1 100644 --- a/app/assets/stylesheets/_page_specific_files.scss +++ b/app/assets/stylesheets/_page_specific_files.scss @@ -17,7 +17,6 @@ @import './pages/pipelines'; @import './pages/profile'; @import './pages/projects'; -@import './pages/prometheus'; @import './pages/registry'; @import './pages/search'; @import './pages/service_desk'; diff --git a/app/assets/stylesheets/page_bundles/incidents.scss b/app/assets/stylesheets/page_bundles/incidents.scss new file mode 100644 index 00000000000..de246fa14b9 --- /dev/null +++ b/app/assets/stylesheets/page_bundles/incidents.scss @@ -0,0 +1,73 @@ +@import 'mixins_and_variables_and_functions'; + +.issuable-discussion.incident-timeline-events { + .main-notes-list::before { + content: none; + } + + .timeline-event-note { + p { + margin-bottom: 0; + font-size: 0.875rem; + } + } +} + +/** + * We have a very specific design proposal where we cannot + * use `vertical-line` mixin as it is and have to use + * custom styles, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81284#note_904867444 + */ +.timeline-entry-vertical-line { + &::before, + &::after { + content: ''; + border-left: 2px solid $gray-50; + position: absolute; + left: 20px; + height: calc(100% + #{$gl-spacing-scale-5}); + top: -#{$gl-spacing-scale-5}; + } + + &:first-child::before { + content: none; + } + + &:first-child { + &::after { + top: $gl-spacing-scale-5; + height: calc(100% + #{$gl-spacing-scale-5}); + } + } + + &:last-child, + &.create-timeline-event { + &::before { + top: - #{$gl-spacing-scale-5} !important; // Override default positioning + @include gl-h-8; + } + + &::after { + content: none; + } + } +} + +.timeline-entry:not(:last-child) { + .timeline-event-border { + @include gl-pb-5; + @include gl-border-gray-50; + @include gl-border-1; + @include gl-border-b-solid; + } +} + +.timeline-group:last-child { + .timeline-entry:last-child, + .create-timeline-event { + .timeline-event-bottom-border { + @include gl-border-b; + @include gl-pt-5; + } + } +} diff --git a/app/assets/stylesheets/pages/prometheus.scss b/app/assets/stylesheets/page_bundles/prometheus.scss index 71cbd7d9613..055036d757d 100644 --- a/app/assets/stylesheets/pages/prometheus.scss +++ b/app/assets/stylesheets/page_bundles/prometheus.scss @@ -1,3 +1,5 @@ +@import 'mixins_and_variables_and_functions'; + .prometheus-graphs { .dropdown-buttons { > div { diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index e77e30c532e..6070311dcb6 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -856,24 +856,6 @@ } } -.issuable-todo-btn { - .gl-spinner { - display: none; - } - - &.is-loading { - .gl-spinner { - display: inline-block; - } - - &.sidebar-collapsed-icon { - .issuable-todo-inner { - display: none; - } - } - } -} - /* * Following overrides are done to prevent * legacy dropdown styles from influencing @@ -932,85 +914,3 @@ } } } - -.icon-overlap-and-shadow { - filter: - drop-shadow(0 1px 0.5px #fff) - drop-shadow(1px 0 0.5px #fff) - drop-shadow(0 -1px 0.5px #fff) - drop-shadow(-1px 0 0.5px #fff); - margin-right: -7px; - z-index: 1; -} - -.issuable-discussion.incident-timeline-events { - .main-notes-list::before { - content: none; - } - - .timeline-event-note { - p { - margin-bottom: 0; - font-size: 0.875rem; - } - } -} - -/** - * We have a very specific design proposal where we cannot - * use `vertical-line` mixin as it is and have to use - * custom styles, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81284#note_904867444 - */ -.timeline-entry-vertical-line { - &::before, - &::after { - content: ''; - border-left: 2px solid $gray-50; - position: absolute; - left: 20px; - height: calc(100% + #{$gl-spacing-scale-5}); - top: -#{$gl-spacing-scale-5}; - } - - &:first-child::before { - content: none; - } - - &:first-child { - &::after { - top: $gl-spacing-scale-5; - height: calc(100% + #{$gl-spacing-scale-5}); - } - } - - &:last-child, - &.create-timeline-event { - &::before { - top: - #{$gl-spacing-scale-5} !important; // Override default positioning - @include gl-h-8; - } - - &::after { - content: none; - } - } -} - -.timeline-entry:not(:last-child) { - .timeline-event-border { - @include gl-pb-5; - @include gl-border-gray-50; - @include gl-border-1; - @include gl-border-b-solid; - } -} - -.timeline-group:last-child { - .timeline-entry:last-child, - .create-timeline-event { - .timeline-event-bottom-border { - @include gl-border-b; - @include gl-pt-5; - } - } -} diff --git a/app/helpers/ci/pipeline_editor_helper.rb b/app/helpers/ci/pipeline_editor_helper.rb index d00301678dd..99a92ba9b59 100644 --- a/app/helpers/ci/pipeline_editor_helper.rb +++ b/app/helpers/ci/pipeline_editor_helper.rb @@ -11,7 +11,6 @@ module Ci def js_pipeline_editor_data(project) initial_branch = params[:branch_name] latest_commit = project.repository.commit(initial_branch) || project.commit - commit_sha = latest_commit ? latest_commit.sha : '' total_branches = project.repository_exists? ? project.repository.branch_count : 0 { @@ -27,17 +26,26 @@ module Ci "lint-unavailable-help-page-path" => help_page_path('ci/pipeline_editor/index', anchor: 'configuration-validation-currently-not-available-message'), "needs-help-page-path" => help_page_path('ci/yaml/index', anchor: 'needs'), "new-merge-request-path" => namespace_project_new_merge_request_path, - "pipeline_etag" => latest_commit ? graphql_etag_pipeline_sha_path(commit_sha) : '', + "pipeline_etag" => latest_commit ? graphql_etag_pipeline_sha_path(latest_commit.sha) : '', "pipeline-page-path" => project_pipelines_path(project), "project-path" => project.path, "project-full-path" => project.full_path, "project-namespace" => project.namespace.full_path, "simulate-pipeline-help-page-path" => help_page_path('ci/pipeline_editor/index', anchor: 'simulate-a-cicd-pipeline'), "total-branches" => total_branches, + "uses-external-config" => uses_external_config?(project) ? 'true' : 'false', "validate-tab-illustration-path" => image_path('illustrations/project-run-CICD-pipelines-sm.svg'), "yml-help-page-path" => help_page_path('ci/yaml/index') } end + + private + + def uses_external_config?(project) + ci_config_source = Gitlab::Ci::ProjectConfig.new(project: project, sha: nil).source + + [:external_project_source, :remote_source].include?(ci_config_source) + end end end diff --git a/app/helpers/ci/pipelines_helper.rb b/app/helpers/ci/pipelines_helper.rb index a67771116b9..c93c8dd8d76 100644 --- a/app/helpers/ci/pipelines_helper.rb +++ b/app/helpers/ci/pipelines_helper.rb @@ -69,7 +69,8 @@ module Ci end def has_pipeline_badges?(pipeline) - pipeline.child? || + pipeline.schedule? || + pipeline.child? || pipeline.latest? || pipeline.merge_train_pipeline? || pipeline.has_yaml_errors? || diff --git a/app/models/project.rb b/app/models/project.rb index aca61f645da..3d026351b4f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -477,6 +477,7 @@ class Project < ApplicationRecord delegate :default_git_depth, :default_git_depth=, to: :ci_cd_settings, prefix: :ci, allow_nil: true delegate :forward_deployment_enabled, :forward_deployment_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true + delegate :inbound_job_token_scope_enabled, :inbound_job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true delegate :keep_latest_artifact, :keep_latest_artifact=, to: :ci_cd_settings, allow_nil: true delegate :opt_in_jwt, :opt_in_jwt=, to: :ci_cd_settings, prefix: :ci, allow_nil: true delegate :allow_fork_pipelines_to_run_in_parent_project, :allow_fork_pipelines_to_run_in_parent_project=, to: :ci_cd_settings, prefix: :ci, allow_nil: true @@ -2720,6 +2721,7 @@ class Project < ApplicationRecord ci_config_path.blank? || ci_config_path == Gitlab::FileDetector::PATTERNS[:gitlab_ci] end + # DO NOT USE. This method will be deprecated soon def uses_external_project_ci_config? !!(ci_config_path =~ %r{@.+/.+}) end @@ -2844,6 +2846,7 @@ class Project < ApplicationRecord repository.gitlab_ci_yml_for(sha, ci_config_path_or_default) end + # DO NOT USE. This method will be deprecated soon def ci_config_external_project Project.find_by_full_path(ci_config_path.split('@', 2).last) end @@ -2892,6 +2895,12 @@ class Project < ApplicationRecord ci_cd_settings.job_token_scope_enabled? end + def ci_inbound_job_token_scope_enabled? + return false unless ci_cd_settings + + ci_cd_settings.inbound_job_token_scope_enabled? + end + def restrict_user_defined_variables? return false unless ci_cd_settings diff --git a/app/models/user.rb b/app/models/user.rb index 9fa9419cd37..d64a52ff7b9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -235,6 +235,7 @@ class User < ApplicationRecord has_one :user_highest_role has_one :user_canonical_email has_one :credit_card_validation, class_name: '::Users::CreditCardValidation' + has_one :phone_number_validation, class_name: '::Users::PhoneNumberValidation' has_one :atlassian_identity, class_name: 'Atlassian::Identity' has_one :banned_user, class_name: '::Users::BannedUser' diff --git a/app/models/users/phone_number_validation.rb b/app/models/users/phone_number_validation.rb new file mode 100644 index 00000000000..f6123c01fd0 --- /dev/null +++ b/app/models/users/phone_number_validation.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Users + class PhoneNumberValidation < ApplicationRecord + self.primary_key = :user_id + self.table_name = 'user_phone_number_validations' + + belongs_to :user, foreign_key: :user_id + belongs_to :banned_user, class_name: '::Users::BannedUser', foreign_key: :user_id + + validates :country, + presence: true, + length: { maximum: 3 } + + validates :international_dial_code, + presence: true, + numericality: { + only_integer: true, + greater_than_or_equal_to: 1, + less_than_or_equal_to: 999 + } + + validates :phone_number, + presence: true, + format: { + with: /\A\d+\Z/, + message: -> (object, data) { _('can contain only digits') } + }, + length: { maximum: 12 } + + validates :telesign_reference_xid, + length: { maximum: 255 } + + def self.related_to_banned_user?(international_dial_code, phone_number) + joins(:banned_user).where( + international_dial_code: international_dial_code, + phone_number: phone_number + ).exists? + end + end +end diff --git a/app/models/wiki.rb b/app/models/wiki.rb index fac79a8194a..afdac86d9c7 100644 --- a/app/models/wiki.rb +++ b/app/models/wiki.rb @@ -229,7 +229,7 @@ class Wiki find_page(SIDEBAR, version) end - def find_file(name, version = 'HEAD', load_content: true) + def find_file(name, version = default_branch, load_content: true) data_limit = load_content ? -1 : 0 blobs = repository.blobs_at([[version, name]], blob_size_limit: data_limit) @@ -423,7 +423,7 @@ class Wiki escaped_title = Regexp.escape(sluggified_title(title)) regex = Regexp.new("^#{escaped_title}\.#{ALLOWED_EXTENSIONS_REGEX}$", 'i') - repository.ls_files('HEAD').any? { |s| s =~ regex } + repository.ls_files(default_branch).any? { |s| s =~ regex } end def raise_duplicate_page_error! @@ -473,11 +473,11 @@ class Wiki end def check_page_historical(path, commit) - repository.last_commit_for_path('HEAD', path).id != commit.id + repository.last_commit_for_path(default_branch, path)&.id != commit&.id end def find_page_with_repository_rpcs(title, version, load_content: true) - version = version.presence || 'HEAD' + version = version.presence || default_branch path = find_matched_file(title, version) return if path.blank? @@ -493,7 +493,7 @@ class Wiki path: sluggified_title(path), raw_data: blob.data, name: canonicalize_filename(path), - historical: version == 'HEAD' ? false : check_page_historical(path, commit), + historical: version == default_branch ? false : check_page_historical(path, commit), version: Gitlab::Git::WikiPageVersion.new(commit, format) ) WikiPage.new(self, page) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 63c60f5a89e..5cdd1fdadcb 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -138,7 +138,7 @@ class WikiPage default_per_page = Kaminari.config.default_per_page offset = [options[:page].to_i - 1, 0].max * options.fetch(:per_page, default_per_page) - wiki.repository.commits('HEAD', + wiki.repository.commits(wiki.default_branch, path: page.path, limit: options.fetch(:limit, default_per_page), offset: offset) @@ -147,7 +147,7 @@ class WikiPage def count_versions return [] unless persisted? - wiki.wiki.count_page_versions(page.path) + wiki.repository.count_commits(ref: wiki.default_branch, path: page.path) end def last_version diff --git a/app/uploaders/object_storage/cdn/google_cdn.rb b/app/uploaders/object_storage/cdn/google_cdn.rb index ea7683f131c..d34ed8ac897 100644 --- a/app/uploaders/object_storage/cdn/google_cdn.rb +++ b/app/uploaders/object_storage/cdn/google_cdn.rb @@ -41,7 +41,7 @@ module ObjectStorage private def config_valid? - [key_name, decoded_key, cdn_url].all?(&:present?) + [key_name, decoded_key, cdn_url].all?(&:present?) && cdn_url.start_with?('https://') end def key_name diff --git a/app/views/clusters/clusters/_health.html.haml b/app/views/clusters/clusters/_health.html.haml index 75609465eb3..50facdf91a2 100644 --- a/app/views/clusters/clusters/_health.html.haml +++ b/app/views/clusters/clusters/_health.html.haml @@ -1,3 +1,5 @@ +- add_page_specific_style 'page_bundles/prometheus' + %section.settings.no-animate.expanded.cluster-health-graphs.qa-cluster-health-section#cluster-health - if @cluster&.integration_prometheus_available? #prometheus-graphs{ data: @cluster.health_data(clusterable) } diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index 4139be053f8..9fd542e0cfb 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -10,7 +10,7 @@ %ul.blob-commit-info = render 'projects/commits/commit', commit: @last_commit, project: @project, ref: @ref - = render_if_exists 'projects/blob/owners', blob: blob + #js-code-owners{ data: { blob_path: blob.path, project_path: @project.full_path, branch: @ref } } = render "projects/blob/auxiliary_viewer", blob: blob #blob-content-holder.blob-content-holder diff --git a/app/views/projects/branch_rules/_show.html.haml b/app/views/projects/branch_rules/_show.html.haml index 46665fdb450..27525b441ab 100644 --- a/app/views/projects/branch_rules/_show.html.haml +++ b/app/views/projects/branch_rules/_show.html.haml @@ -9,4 +9,4 @@ = _('Define rules for who can push, merge, and the required approvals for each branch.') .settings-content.gl-pr-0 - #js-branch-rules{ data: { project_path: @project.full_path } } + #js-branch-rules{ data: { project_path: @project.full_path, branch_rules_path: project_settings_repository_branch_rules_path(@project) } } diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index cd7339edd1a..31041d124e4 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -1,3 +1,5 @@ +- add_page_specific_style 'page_bundles/prometheus' + - page_title _("Metrics Dashboard"), @environment.name .prometheus-container diff --git a/app/views/projects/incidents/show.html.haml b/app/views/projects/incidents/show.html.haml index 5043f94bd5c..7a1e7f503f8 100644 --- a/app/views/projects/incidents/show.html.haml +++ b/app/views/projects/incidents/show.html.haml @@ -2,6 +2,7 @@ - add_to_breadcrumbs _("Incidents"), project_incidents_path(@project) - breadcrumb_title @issue.to_reference - page_title "#{@issue.title} (#{@issue.to_reference})", _("Incidents") +- add_page_specific_style 'page_bundles/incidents' - add_page_specific_style 'page_bundles/issues_show' = render 'projects/issuable/show', issuable: @issue diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 06c422fc4d6..76b725d140c 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -2,6 +2,7 @@ - add_to_breadcrumbs _("Issues"), project_issues_path(@project) - breadcrumb_title @issue.to_reference - page_title "#{@issue.title} (#{@issue.to_reference})", _("Issues") +- add_page_specific_style 'page_bundles/incidents' - add_page_specific_style 'page_bundles/issues_show' - add_page_specific_style 'page_bundles/work_items' diff --git a/app/views/projects/mirrors/_authentication_method.html.haml b/app/views/projects/mirrors/_authentication_method.html.haml index 28b433b2514..4b549aaf1cd 100644 --- a/app/views/projects/mirrors/_authentication_method.html.haml +++ b/app/views/projects/mirrors/_authentication_method.html.haml @@ -3,12 +3,10 @@ .form-group = f.label :auth_method, _('Authentication method'), class: 'label-bold' - .select-wrapper - = f.select :auth_method, - options_for_select(auth_options, mirror.auth_method), - {}, { class: "form-control gl-form-select select-control js-mirror-auth-type qa-authentication-method" } - = sprite_icon('chevron-down', css_class: "gl-icon gl-absolute gl-top-3 gl-right-3 gl-text-gray-200") - = f.hidden_field :auth_method, value: "password", class: "js-hidden-mirror-auth-type" + = f.select :auth_method, + options_for_select(auth_options, mirror.auth_method), + {}, { class: "custom-select gl-form-select js-mirror-auth-type qa-authentication-method" } + = f.hidden_field :auth_method, value: "password", class: "js-hidden-mirror-auth-type" .form-group .well-password-auth.collapse.js-well-password-auth diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 5db898067db..2e403358e2e 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -22,6 +22,8 @@ .well-segment.qa-pipeline-badges .icon-container = sprite_icon('flag', css_class: 'gl-top-0!') + - if @pipeline.schedule? + = gl_badge_tag _('Scheduled'), { variant: :info, size: :sm }, { class: 'js-pipeline-url-scheduled', title: _('This pipeline was triggered by a schedule.') } - if @pipeline.child? - text = sprintf(s_('Pipelines|Child pipeline (%{link_start}parent%{link_end})'), { link_start: "<a href='#{pipeline_path(@pipeline.triggered_by_pipeline)}' class='text-underline'>", link_end: "</a>"}).html_safe = gl_badge_tag text, { variant: :info, size: :sm }, { class: 'js-pipeline-child has-tooltip', title: s_("Pipelines|This is a child pipeline within the parent pipeline") } diff --git a/app/views/projects/settings/branch_rules/index.html.haml b/app/views/projects/settings/branch_rules/index.html.haml index 84c060701bb..ab692a23e44 100644 --- a/app/views/projects/settings/branch_rules/index.html.haml +++ b/app/views/projects/settings/branch_rules/index.html.haml @@ -1,6 +1,6 @@ - add_to_breadcrumbs _('Repository Settings'), project_settings_repository_path(@project) - page_title s_('BranchRules|Branch rules details') -%h3= s_('BranchRules|Branch rules details') +%h3.gl-mb-5= s_('BranchRules|Branch rules details') -#js-branch-rules{ data: { project_path: @project.full_path } } +#js-branch-rules{ data: { project_path: @project.full_path, protected_branches_path: project_settings_repository_path(@project, anchor: 'js-protected-branches-settings') } } |