diff options
106 files changed, 1472 insertions, 287 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 8f8c0c65eb4..7a5516338e8 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -92,6 +92,51 @@ /doc/user/search/advanced_global_search.md @marcia /doc/user/search/advanced_search_syntax.md @marcia /doc/user/search/index.md @marcia +/doc/administration/file_hooks.md @marcia +/doc/administration/git_annex.md @marcia +/doc/administration/git_protocol.md @marcia +/doc/administration/integration/plantuml.md @marcia +/doc/administration/invalidate_markdown_cache.md @marcia +/doc/administration/issue_closing_pattern.md @marcia +/doc/administration/lfs/index.md @marcia +/doc/administration/merge_request_diffs.md @marcia +/doc/administration/repository_checks.md @marcia +/doc/administration/snippets/index.md @marcia +/doc/administration/static_objects_external_storage.md @marcia +/doc/api/access_requests.md @marcia +/doc/api/branches.md @marcia +/doc/api/commits.md @marcia +/doc/api/discussions.md @marcia +/doc/api/group_wikis.md @marcia +/doc/api/keys.md @marcia +/doc/api/markdown.md @marcia +/doc/api/merge_request_approvals.md @marcia +/doc/api/merge_request_context_commits.md @marcia +/doc/api/merge_requests.md @marcia +/doc/api/project_aliases.md @marcia +/doc/api/project_badges.md @marcia +/doc/api/project_import_export.md @marcia +/doc/api/project_level_variables.md @marcia +/doc/api/project_snippets.md @marcia +/doc/api/project_statistics.md @marcia +/doc/api/project_templates.md @marcia +/doc/api/project_vulnerabilities.md @marcia +/doc/api/protected_branches.md @marcia +/doc/api/protected_tags.md @marcia +/doc/api/remote_mirrors.md @marcia +/doc/api/repositories.md @marcia +/doc/api/repository_files.md @marcia +/doc/api/repository_submodules.md @marcia +/doc/api/search.md @marcia +/doc/api/snippets.md @marcia +/doc/api/suggestions.md @marcia +/doc/api/tags.md @marcia +/doc/api/visual_review_discussions.md @marcia +/doc/api/wikis.md @marcia +/doc/user/admin_area/settings/account_and_limit_settings.md @marcia +/doc/user/admin_area/settings/instance_template_repository.md @marcia +/doc/user/admin_area/settings/push_event_activities_limit.md @marcia +/doc/user/admin_area/settings/visibility_and_access_controls.md @marcia [Frontend] *.scss @annabeldunstone @gitlab-org/maintainers/frontend diff --git a/app/assets/javascripts/boards/components/board_settings_sidebar.vue b/app/assets/javascripts/boards/components/board_settings_sidebar.vue new file mode 100644 index 00000000000..b0256378dce --- /dev/null +++ b/app/assets/javascripts/boards/components/board_settings_sidebar.vue @@ -0,0 +1,126 @@ +<script> +import { GlDrawer, GlLabel, GlAvatarLink, GlAvatarLabeled, GlLink } from '@gitlab/ui'; +import { mapActions, mapState } from 'vuex'; +import { __ } from '~/locale'; +import boardsStore from '~/boards/stores/boards_store'; +import eventHub from '~/sidebar/event_hub'; +import { isScopedLabel } from '~/lib/utils/common_utils'; +import { inactiveId } from '~/boards/constants'; + +// NOTE: need to revisit how we handle headerHeight, because we have so many different header and footer options. +export default { + headerHeight: process.env.NODE_ENV === 'development' ? '75px' : '40px', + listSettingsText: __('List settings'), + assignee: 'assignee', + milestone: 'milestone', + label: 'label', + labelListText: __('Label'), + labelMilestoneText: __('Milestone'), + labelAssigneeText: __('Assignee'), + components: { + GlDrawer, + GlLabel, + GlAvatarLink, + GlAvatarLabeled, + GlLink, + BoardSettingsSidebarWipLimit: () => + import('ee_component/boards/components/board_settings_wip_limit.vue'), + }, + computed: { + ...mapState(['activeId']), + activeList() { + /* + Warning: Though a computed property it is not reactive because we are + referencing a List Model class. Reactivity only applies to plain JS objects + */ + return boardsStore.state.lists.find(({ id }) => id === this.activeId); + }, + isSidebarOpen() { + return this.activeId !== inactiveId; + }, + activeListLabel() { + return this.activeList.label; + }, + activeListMilestone() { + return this.activeList.milestone; + }, + activeListAssignee() { + return this.activeList.assignee; + }, + boardListType() { + return this.activeList.type || null; + }, + listTypeTitle() { + switch (this.boardListType) { + case this.$options.milestone: { + return this.$options.labelMilestoneText; + } + case this.$options.label: { + return this.$options.labelListText; + } + case this.$options.assignee: { + return this.$options.labelAssigneeText; + } + default: { + return ''; + } + } + }, + }, + created() { + eventHub.$on('sidebar.closeAll', this.closeSidebar); + }, + beforeDestroy() { + eventHub.$off('sidebar.closeAll', this.closeSidebar); + }, + methods: { + ...mapActions(['setActiveId']), + closeSidebar() { + this.setActiveId(inactiveId); + }, + showScopedLabels(label) { + return boardsStore.scopedLabels.enabled && isScopedLabel(label); + }, + }, +}; +</script> + +<template> + <gl-drawer + class="js-board-settings-sidebar" + :open="isSidebarOpen" + :header-height="$options.headerHeight" + @close="closeSidebar" + > + <template #header>{{ $options.listSettingsText }}</template> + <template v-if="isSidebarOpen"> + <div class="d-flex flex-column align-items-start"> + <label class="js-list-label">{{ listTypeTitle }}</label> + <template v-if="boardListType === $options.label"> + <gl-label + :title="activeListLabel.title" + :background-color="activeListLabel.color" + :scoped="showScopedLabels(activeListLabel)" + /> + </template> + <template v-else-if="boardListType === $options.assignee"> + <gl-avatar-link class="js-assignee" :href="activeListAssignee.webUrl"> + <gl-avatar-labeled + :size="32" + :label="activeListAssignee.name" + :sub-label="`@${activeListAssignee.username}`" + :src="activeListAssignee.avatar" + /> + </gl-avatar-link> + </template> + <template v-else-if="boardListType === $options.milestone"> + <gl-link class="js-milestone" :href="activeListMilestone.webUrl"> + {{ activeListMilestone.title }} + </gl-link> + </template> + </div> + + <board-settings-sidebar-wip-limit :max-issue-count="activeList.maxIssueCount" /> + </template> + </gl-drawer> +</template> diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 5b4a1d262dd..7bc8a8a3b0e 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -83,8 +83,7 @@ export default () => { Board: () => import('ee_else_ce/boards/components/board_column.vue'), BoardSidebar, BoardAddIssuesModal, - BoardSettingsSidebar: () => - import('ee_component/boards/components/board_settings_sidebar.vue'), + BoardSettingsSidebar: () => import('~/boards/components/board_settings_sidebar.vue'), }, store, apolloProvider, diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index 08fedb14dff..49d2a5c546f 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -10,6 +10,10 @@ export default { commit(types.SET_ENDPOINTS, endpoints); }, + setActiveId({ commit }, id) { + commit(types.SET_ACTIVE_ID, id); + }, + fetchLists: () => { notImplemented(); }, diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js index fcdfa6799b6..8deda166909 100644 --- a/app/assets/javascripts/boards/stores/mutation_types.js +++ b/app/assets/javascripts/boards/stores/mutation_types.js @@ -19,3 +19,4 @@ export const RECEIVE_UPDATE_ISSUE_SUCCESS = 'RECEIVE_UPDATE_ISSUE_SUCCESS'; export const RECEIVE_UPDATE_ISSUE_ERROR = 'RECEIVE_UPDATE_ISSUE_ERROR'; export const SET_CURRENT_PAGE = 'SET_CURRENT_PAGE'; export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE'; +export const SET_ACTIVE_ID = 'SET_ACTIVE_ID'; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index e4459cdcc07..0cf4080cf35 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -10,6 +10,10 @@ export default { state.endpoints = endpoints; }, + [mutationTypes.SET_ACTIVE_ID](state, id) { + state.activeId = id; + }, + [mutationTypes.REQUEST_ADD_LIST]: () => { notImplemented(); }, diff --git a/app/assets/javascripts/design_management/components/design_sidebar.vue b/app/assets/javascripts/design_management/components/design_sidebar.vue index 333ad2557e8..1970f03a6f3 100644 --- a/app/assets/javascripts/design_management/components/design_sidebar.vue +++ b/app/assets/javascripts/design_management/components/design_sidebar.vue @@ -48,7 +48,7 @@ export default { }; }, discussionParticipants() { - return extractParticipants(this.issue.participants); + return extractParticipants(this.issue.participants.nodes); }, resolvedDiscussions() { return this.discussions.filter(discussion => discussion.resolved); diff --git a/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue b/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue index 73ad1a17aa8..1c6d8cbe8e3 100644 --- a/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue +++ b/app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue @@ -18,7 +18,7 @@ export default { if (!this.queryVersion) return 0; const idx = this.allVersions.findIndex( - version => this.findVersionId(version.node.id) === this.queryVersion, + version => this.findVersionId(version.id) === this.queryVersion, ); // if the currentVersionId isn't a valid version (i.e. not in allVersions) @@ -29,7 +29,7 @@ export default { if (this.queryVersion) return this.queryVersion; const currentVersion = this.allVersions[this.currentVersionIdx]; - return this.findVersionId(currentVersion.node.id); + return this.findVersionId(currentVersion.id); }, dropdownText() { if (this.isLatestVersion) { @@ -51,23 +51,21 @@ export default { <template> <gl-new-dropdown :text="dropdownText" size="small" class="design-version-dropdown"> - <gl-new-dropdown-item v-for="(version, index) in allVersions" :key="version.node.id"> + <gl-new-dropdown-item v-for="(version, index) in allVersions" :key="version.id"> <router-link class="d-flex js-version-link" - :to="{ path: $route.path, query: { version: findVersionId(version.node.id) } }" + :to="{ path: $route.path, query: { version: findVersionId(version.id) } }" > <div class="flex-grow-1 ml-2"> <div> <strong >{{ __('Version') }} {{ allVersions.length - index }} - <span v-if="findVersionId(version.node.id) === latestVersionId" - >({{ __('latest') }})</span - > + <span v-if="findVersionId(version.id) === latestVersionId">({{ __('latest') }})</span> </strong> </div> </div> <i - v-if="findVersionId(version.node.id) === currentVersionId" + v-if="findVersionId(version.id) === currentVersionId" class="fa fa-check float-right gl-mr-2" ></i> </router-link> diff --git a/app/assets/javascripts/design_management/graphql/mutations/create_image_diff_note.mutation.graphql b/app/assets/javascripts/design_management/graphql/mutations/create_image_diff_note.mutation.graphql index c8ade328120..0b8400ac040 100644 --- a/app/assets/javascripts/design_management/graphql/mutations/create_image_diff_note.mutation.graphql +++ b/app/assets/javascripts/design_management/graphql/mutations/create_image_diff_note.mutation.graphql @@ -8,10 +8,8 @@ mutation createImageDiffNote($input: CreateImageDiffNoteInput!) { id replyId notes { - edges { - node { - ...DesignNote - } + nodes { + ...DesignNote } } } diff --git a/app/assets/javascripts/design_management/graphql/mutations/upload_design.mutation.graphql b/app/assets/javascripts/design_management/graphql/mutations/upload_design.mutation.graphql index d694e6558a0..84aeb374351 100644 --- a/app/assets/javascripts/design_management/graphql/mutations/upload_design.mutation.graphql +++ b/app/assets/javascripts/design_management/graphql/mutations/upload_design.mutation.graphql @@ -5,11 +5,9 @@ mutation uploadDesign($files: [Upload!]!, $projectPath: ID!, $iid: ID!) { designs { ...DesignItem versions { - edges { - node { - id - sha - } + nodes { + id + sha } } } diff --git a/app/assets/javascripts/design_management/graphql/queries/get_design.query.graphql b/app/assets/javascripts/design_management/graphql/queries/get_design.query.graphql index 07a9af55787..ab987dda525 100644 --- a/app/assets/javascripts/design_management/graphql/queries/get_design.query.graphql +++ b/app/assets/javascripts/design_management/graphql/queries/get_design.query.graphql @@ -7,19 +7,15 @@ query getDesign($fullPath: ID!, $iid: String!, $atVersion: ID, $filenames: [Stri issue(iid: $iid) { designCollection { designs(atVersion: $atVersion, filenames: $filenames) { - edges { - node { - ...DesignItem - issue { - title - webPath - webUrl - participants { - edges { - node { - ...Author - } - } + nodes { + ...DesignItem + issue { + title + webPath + webUrl + participants { + nodes { + ...Author } } } diff --git a/app/assets/javascripts/design_management/graphql/queries/get_design_list.query.graphql b/app/assets/javascripts/design_management/graphql/queries/get_design_list.query.graphql index 121a50555b3..96efa8e8242 100644 --- a/app/assets/javascripts/design_management/graphql/queries/get_design_list.query.graphql +++ b/app/assets/javascripts/design_management/graphql/queries/get_design_list.query.graphql @@ -7,17 +7,13 @@ query getDesignList($fullPath: ID!, $iid: String!, $atVersion: ID) { issue(iid: $iid) { designCollection { designs(atVersion: $atVersion) { - edges { - node { - ...DesignListItem - } + nodes { + ...DesignListItem } } versions { - edges { - node { - ...VersionListItem - } + nodes { + ...VersionListItem } } } diff --git a/app/assets/javascripts/design_management/mixins/all_designs.js b/app/assets/javascripts/design_management/mixins/all_designs.js index f7d6551c46c..a1935adefbf 100644 --- a/app/assets/javascripts/design_management/mixins/all_designs.js +++ b/app/assets/javascripts/design_management/mixins/all_designs.js @@ -2,7 +2,6 @@ import { propertyOf } from 'lodash'; import createFlash from '~/flash'; import { s__ } from '~/locale'; import getDesignListQuery from '../graphql/queries/get_design_list.query.graphql'; -import { extractNodes } from '../utils/design_management_utils'; import allVersionsMixin from './all_versions'; import { DESIGNS_ROUTE_NAME } from '../router/constants'; @@ -19,9 +18,15 @@ export default { }; }, update: data => { - const designEdges = propertyOf(data)(['project', 'issue', 'designCollection', 'designs']); - if (designEdges) { - return extractNodes(designEdges); + const designNodes = propertyOf(data)([ + 'project', + 'issue', + 'designCollection', + 'designs', + 'nodes', + ]); + if (designNodes) { + return designNodes; } return []; }, diff --git a/app/assets/javascripts/design_management/mixins/all_versions.js b/app/assets/javascripts/design_management/mixins/all_versions.js index 99e2ee9561c..7a094f23378 100644 --- a/app/assets/javascripts/design_management/mixins/all_versions.js +++ b/app/assets/javascripts/design_management/mixins/all_versions.js @@ -12,7 +12,7 @@ export default { atVersion: null, }; }, - update: data => data.project.issue.designCollection.versions.edges, + update: data => data.project.issue.designCollection.versions.nodes, }, }, inject: { @@ -28,7 +28,7 @@ export default { return ( this.$route.query.version && this.allVersions && - this.allVersions.some(version => version.node.id.endsWith(this.$route.query.version)) + this.allVersions.some(version => version.id.endsWith(this.$route.query.version)) ); }, designsVersion() { @@ -38,7 +38,7 @@ export default { }, latestVersionId() { const latestVersion = this.allVersions[0]; - return latestVersion && findVersionId(latestVersion.node.id); + return latestVersion && findVersionId(latestVersion.id); }, isLatestVersion() { if (this.allVersions.length > 0) { diff --git a/app/assets/javascripts/design_management/utils/cache_update.js b/app/assets/javascripts/design_management/utils/cache_update.js index 24b374b79fd..74ca59ad5c7 100644 --- a/app/assets/javascripts/design_management/utils/cache_update.js +++ b/app/assets/javascripts/design_management/utils/cache_update.js @@ -12,10 +12,10 @@ import { const deleteDesignsFromStore = (store, query, selectedDesigns) => { const data = store.readQuery(query); - const changedDesigns = data.project.issue.designCollection.designs.edges.filter( - ({ node }) => !selectedDesigns.includes(node.filename), + const changedDesigns = data.project.issue.designCollection.designs.nodes.filter( + node => !selectedDesigns.includes(node.filename), ); - data.project.issue.designCollection.designs.edges = [...changedDesigns]; + data.project.issue.designCollection.designs.nodes = [...changedDesigns]; store.writeQuery({ ...query, @@ -34,11 +34,10 @@ const addNewVersionToStore = (store, query, version) => { if (!version) return; const data = store.readQuery(query); - const newEdge = { node: version, __typename: 'DesignVersionEdge' }; - data.project.issue.designCollection.versions.edges = [ - newEdge, - ...data.project.issue.designCollection.versions.edges, + data.project.issue.designCollection.versions.nodes = [ + version, + ...data.project.issue.designCollection.versions.nodes, ]; store.writeQuery({ @@ -59,18 +58,15 @@ const addDiscussionCommentToStore = (store, createNote, query, queryVariables, d design.notesCount += 1; if ( - !design.issue.participants.edges.some( - participant => participant.node.username === createNote.note.author.username, + !design.issue.participants.nodes.some( + participant => participant.username === createNote.note.author.username, ) ) { - design.issue.participants.edges = [ - ...design.issue.participants.edges, + design.issue.participants.nodes = [ + ...design.issue.participants.nodes, { - __typename: 'UserEdge', - node: { - __typename: 'User', - ...createNote.note.author, - }, + __typename: 'User', + ...createNote.note.author, }, ]; } @@ -108,18 +104,15 @@ const addImageDiffNoteToStore = (store, createImageDiffNote, query, variables) = const notesCount = design.notesCount + 1; design.discussions.nodes = [...design.discussions.nodes, newDiscussion]; if ( - !design.issue.participants.edges.some( - participant => participant.node.username === createImageDiffNote.note.author.username, + !design.issue.participants.nodes.some( + participant => participant.username === createImageDiffNote.note.author.username, ) ) { - design.issue.participants.edges = [ - ...design.issue.participants.edges, + design.issue.participants.nodes = [ + ...design.issue.participants.nodes, { - __typename: 'UserEdge', - node: { - __typename: 'User', - ...createImageDiffNote.note.author, - }, + __typename: 'User', + ...createImageDiffNote.note.author, }, ]; } @@ -166,9 +159,9 @@ const updateImageDiffNoteInStore = (store, updateImageDiffNote, query, variables const addNewDesignToStore = (store, designManagementUpload, query) => { const data = store.readQuery(query); - const newDesigns = data.project.issue.designCollection.designs.edges.reduce((acc, design) => { - if (!acc.find(d => d.filename === design.node.filename)) { - acc.push(design.node); + const newDesigns = data.project.issue.designCollection.designs.nodes.reduce((acc, design) => { + if (!acc.find(d => d.filename === design.filename)) { + acc.push(design); } return acc; @@ -178,30 +171,27 @@ const addNewDesignToStore = (store, designManagementUpload, query) => { const findNewVersions = designManagementUpload.designs.find(design => design.versions); if (findNewVersions) { - const findNewVersionsEdges = findNewVersions.versions.edges; + const findNewVersionsNodes = findNewVersions.versions.nodes; - if (findNewVersionsEdges && findNewVersionsEdges.length) { - newVersionNode = [findNewVersionsEdges[0]]; + if (findNewVersionsNodes && findNewVersionsNodes.length) { + newVersionNode = [findNewVersionsNodes[0]]; } } const newVersions = [ ...(newVersionNode || []), - ...data.project.issue.designCollection.versions.edges, + ...data.project.issue.designCollection.versions.nodes, ]; const updatedDesigns = { __typename: 'DesignCollection', designs: { __typename: 'DesignConnection', - edges: newDesigns.map(design => ({ - __typename: 'DesignEdge', - node: design, - })), + nodes: newDesigns, }, versions: { __typename: 'DesignVersionConnection', - edges: newVersions, + nodes: newVersions, }, }; diff --git a/app/assets/javascripts/design_management/utils/design_management_utils.js b/app/assets/javascripts/design_management/utils/design_management_utils.js index 22705cf67a1..c7fb508b4d7 100644 --- a/app/assets/javascripts/design_management/utils/design_management_utils.js +++ b/app/assets/javascripts/design_management/utils/design_management_utils.js @@ -5,17 +5,7 @@ export const isValidDesignFile = ({ type }) => (type.match(VALID_DESIGN_FILE_MIMETYPE.regex) || []).length > 0; /** - * Returns formatted array that doesn't contain - * `edges`->`node` nesting - * - * @param {Array} elements - */ - -export const extractNodes = elements => elements.edges.map(({ node }) => node); - -/** - * Returns formatted array of discussions that doesn't contain - * `edges`->`node` nesting for child notes + * Returns formatted array of discussions * * @param {Array} discussions */ @@ -40,9 +30,9 @@ export const findVersionId = id => (id.match('::Version/(.+$)') || [])[1]; export const findNoteId = id => (id.match('DiffNote/(.+$)') || [])[1]; -export const extractDesigns = data => data.project.issue.designCollection.designs.edges; +export const extractDesigns = data => data.project.issue.designCollection.designs.nodes; -export const extractDesign = data => (extractDesigns(data) || [])[0]?.node; +export const extractDesign = data => (extractDesigns(data) || [])[0]; /** * Generates optimistic response for a design upload mutation @@ -72,13 +62,10 @@ export const designUploadOptimisticResponse = files => { }, versions: { __typename: 'DesignVersionConnection', - edges: { - __typename: 'DesignVersionEdge', - node: { - __typename: 'DesignVersion', - id: -uniqueId(), - sha: -uniqueId(), - }, + nodes: { + __typename: 'DesignVersion', + id: -uniqueId(), + sha: -uniqueId(), }, }, })); @@ -123,6 +110,6 @@ const normalizeAuthor = author => ({ avatar_url: author.avatarUrl, }); -export const extractParticipants = users => users.edges.map(({ node }) => normalizeAuthor(node)); +export const extractParticipants = users => users.map(node => normalizeAuthor(node)); export const getPageLayoutElement = () => document.querySelector('.layout-page'); diff --git a/app/assets/javascripts/packages/details/components/app.vue b/app/assets/javascripts/packages/details/components/app.vue index d2b2610892f..d48f9651b59 100644 --- a/app/assets/javascripts/packages/details/components/app.vue +++ b/app/assets/javascripts/packages/details/components/app.vue @@ -14,6 +14,7 @@ import { } from '@gitlab/ui'; import Tracking from '~/tracking'; import PackageActivity from './activity.vue'; +import PackageHistory from './package_history.vue'; import PackageInformation from './information.vue'; import PackageTitle from './package_title.vue'; import ConanInstallation from './conan_installation.vue'; @@ -57,6 +58,7 @@ export default { PackagesListLoader, PackageListRow, DependencyRow, + PackageHistory, }, directives: { GlTooltip: GlTooltipDirective, @@ -66,6 +68,7 @@ export default { trackingActions: { ...TrackingActions }, computed: { ...mapState([ + 'projectName', 'packageEntity', 'packageFiles', 'isLoading', @@ -74,6 +77,7 @@ export default { 'svgPath', 'npmPath', 'npmHelpPath', + 'oneColumnView', ]), installationComponent() { switch (this.packageEntity.package_type) { @@ -219,29 +223,37 @@ export default { <gl-tabs> <gl-tab :title="__('Detail')"> - <div class="row" data-qa-selector="package_information_content"> - <div class="col-sm-6"> - <package-information :information="packageInformation" /> - <package-information - v-if="packageMetadata" - :heading="packageMetadataTitle" - :information="packageMetadata" - :show-copy="true" - /> - </div> + <template v-if="!oneColumnView"> + <div + class="row" + data-qa-selector="package_information_content" + data-testid="old-package-info" + > + <div class="col-sm-6"> + <package-information :information="packageInformation" /> + <package-information + v-if="packageMetadata" + :heading="packageMetadataTitle" + :information="packageMetadata" + :show-copy="true" + /> + </div> - <div class="col-sm-6"> - <component - :is="installationComponent" - v-if="installationComponent" - :name="packageEntity.name" - :registry-url="npmPath" - :help-url="npmHelpPath" - /> + <div class="col-sm-6"> + <component + :is="installationComponent" + v-if="installationComponent" + :name="packageEntity.name" + :registry-url="npmPath" + :help-url="npmHelpPath" + /> + </div> </div> - </div> - <package-activity /> + <package-activity /> + </template> + + <package-history v-else :package-entity="packageEntity" :project-name="projectName" /> <h3 class="gl-font-lg">{{ __('Files') }}</h3> <gl-table diff --git a/app/assets/javascripts/packages/details/components/history_element.vue b/app/assets/javascripts/packages/details/components/history_element.vue new file mode 100644 index 00000000000..f2129ec9c29 --- /dev/null +++ b/app/assets/javascripts/packages/details/components/history_element.vue @@ -0,0 +1,35 @@ +<script> +import { GlIcon } from '@gitlab/ui'; +import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; + +export default { + name: 'HistoryElement', + components: { + GlIcon, + TimelineEntryItem, + }, + + props: { + icon: { + type: String, + required: true, + }, + }, +}; +</script> + +<template> + <timeline-entry-item class="system-note note-wrapper gl-my-6!"> + <div class="timeline-icon"> + <gl-icon :name="icon" /> + </div> + <div class="timeline-content"> + <div class="note-header"> + <span> + <slot></slot> + </span> + </div> + <div class="note-body"></div> + </div> + </timeline-entry-item> +</template> diff --git a/app/assets/javascripts/packages/details/components/package_history.vue b/app/assets/javascripts/packages/details/components/package_history.vue new file mode 100644 index 00000000000..a3f3e85a1fb --- /dev/null +++ b/app/assets/javascripts/packages/details/components/package_history.vue @@ -0,0 +1,114 @@ +<script> +import { GlLink, GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import HistoryElement from './history_element.vue'; + +export default { + name: 'PackageHistory', + i18n: { + createdOn: s__('PackageRegistry|%{name} version %{version} was created %{datetime}'), + updatedAtText: s__('PackageRegistry|%{name} version %{version} was updated %{datetime}'), + commitText: s__('PackageRegistry|Commit %{link} on branch %{branch}'), + pipelineText: s__('PackageRegistry|Pipeline %{link} triggered %{datetime} by %{author}'), + publishText: s__('PackageRegistry|Published to the %{project} Package Registry %{datetime}'), + }, + components: { + GlLink, + GlSprintf, + HistoryElement, + TimeAgoTooltip, + }, + props: { + packageEntity: { + type: Object, + required: true, + }, + projectName: { + type: String, + required: true, + }, + }, + data() { + return { + showDescription: false, + }; + }, + computed: { + packagePipeline() { + return this.packageEntity.pipeline?.id ? this.packageEntity.pipeline : null; + }, + }, +}; +</script> + +<template> + <div class="issuable-discussion"> + <h3 class="gl-ml-6" data-testid="title">{{ __('History') }}</h3> + <ul class="timeline main-notes-list notes gl-my-4" data-testid="timeline"> + <history-element icon="clock" data-testid="created-on"> + <gl-sprintf :message="$options.i18n.createdOn"> + <template #name> + <strong>{{ packageEntity.name }}</strong> + </template> + <template #version> + <strong>{{ packageEntity.version }}</strong> + </template> + <template #datetime> + <time-ago-tooltip :time="packageEntity.created_at" /> + </template> + </gl-sprintf> + </history-element> + <history-element icon="pencil" data-testid="updated-at"> + <gl-sprintf :message="$options.i18n.updatedAtText"> + <template #name> + <strong>{{ packageEntity.name }}</strong> + </template> + <template #version> + <strong>{{ packageEntity.version }}</strong> + </template> + <template #datetime> + <time-ago-tooltip :time="packageEntity.updated_at" /> + </template> + </gl-sprintf> + </history-element> + <template v-if="packagePipeline"> + <history-element icon="commit" data-testid="commit"> + <gl-sprintf :message="$options.i18n.commitText"> + <template #link> + <gl-link :href="`../../commit/${packagePipeline.sha}`">{{ + packagePipeline.sha + }}</gl-link> + </template> + <template #branch> + <strong>{{ packagePipeline.ref }}</strong> + </template> + </gl-sprintf> + </history-element> + <history-element icon="pipeline" data-testid="pipeline"> + <gl-sprintf :message="$options.i18n.pipelineText"> + <template #link> + <gl-link :href="`../../pipelines/${packagePipeline.id}`" + >#{{ packagePipeline.id }}</gl-link + > + </template> + <template #datetime> + <time-ago-tooltip :time="packagePipeline.created_at" /> + </template> + <template #author>{{ packagePipeline.user.name }}</template> + </gl-sprintf> + </history-element> + </template> + <history-element icon="package" data-testid="published"> + <gl-sprintf :message="$options.i18n.publishText"> + <template #project> + <strong>{{ projectName }}</strong> + </template> + <template #datetime> + <time-ago-tooltip :time="packageEntity.created_at" /> + </template> + </gl-sprintf> + </history-element> + </ul> + </div> +</template> diff --git a/app/assets/javascripts/packages/details/index.js b/app/assets/javascripts/packages/details/index.js index 233da3e4a99..44f6228f0b0 100644 --- a/app/assets/javascripts/packages/details/index.js +++ b/app/assets/javascripts/packages/details/index.js @@ -1,4 +1,5 @@ import Vue from 'vue'; +import { parseBoolean } from '~/lib/utils/common_utils'; import PackagesApp from './components/app.vue'; import Translate from '~/vue_shared/translate'; import createStore from './store'; @@ -7,7 +8,7 @@ Vue.use(Translate); export default () => { const el = document.querySelector('#js-vue-packages-detail'); - const { package: packageJson, canDelete: canDeleteStr, ...rest } = el.dataset; + const { package: packageJson, canDelete: canDeleteStr, oneColumnView, ...rest } = el.dataset; const packageEntity = JSON.parse(packageJson); const canDelete = canDeleteStr === 'true'; @@ -15,6 +16,7 @@ export default () => { packageEntity, packageFiles: packageEntity.package_files, canDelete, + oneColumnView: parseBoolean(oneColumnView), ...rest, }); diff --git a/app/assets/javascripts/repository/components/preview/index.vue b/app/assets/javascripts/repository/components/preview/index.vue index 7701ebfa9eb..013092ffefd 100644 --- a/app/assets/javascripts/repository/components/preview/index.vue +++ b/app/assets/javascripts/repository/components/preview/index.vue @@ -51,7 +51,7 @@ export default { <div class="js-file-title file-title-flex-parent"> <div class="file-header-content"> <i aria-hidden="true" class="fa fa-file-text-o fa-fw"></i> - <gl-link :href="blob.webUrl"> + <gl-link :href="blob.webPath"> <strong>{{ blob.name }}</strong> </gl-link> </div> diff --git a/app/assets/javascripts/repository/queries/path_last_commit.query.graphql b/app/assets/javascripts/repository/queries/path_last_commit.query.graphql index b1fc5b03ebb..51f3f790a5d 100644 --- a/app/assets/javascripts/repository/queries/path_last_commit.query.graphql +++ b/app/assets/javascripts/repository/queries/path_last_commit.query.graphql @@ -8,14 +8,14 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) { titleHtml descriptionHtml message - webUrl + webPath authoredDate authorName authorGravatar author { name avatarUrl - webUrl + webPath } signatureHtml pipelines(ref: $ref, first: 1) { diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index fc53b5fafaf..56b6a5201e7 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -114,4 +114,15 @@ class SearchController < ApplicationController Gitlab::UsageDataCounters::SearchCounter.count(:navbar_searches) end + + def append_info_to_payload(payload) + super + + # Merging to :metadata will ensure these are logged as top level keys + payload[:metadata] || {} + payload[:metadata]['meta.search.group_id'] = params[:group_id] + payload[:metadata]['meta.search.project_id'] = params[:project_id] + payload[:metadata]['meta.search.search'] = params[:search] + payload[:metadata]['meta.search.scope'] = params[:scope] + end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 527d5636e37..848883dc20d 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -524,8 +524,6 @@ module Ci end end - CI_REGISTRY_USER = 'gitlab-ci-token' - def persisted_variables Gitlab::Ci::Variables::Collection.new.tap do |variables| break variables unless persisted? @@ -537,7 +535,7 @@ module Ci .append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false, masked: true) .append(key: 'CI_BUILD_ID', value: id.to_s) .append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false, masked: true) - .append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER) + .append(key: 'CI_REGISTRY_USER', value: ::Gitlab::Auth::CI_REGISTRY_USER) .append(key: 'CI_REGISTRY_PASSWORD', value: token.to_s, public: false, masked: true) .append(key: 'CI_REPOSITORY_URL', value: repo_url.to_s, public: false) .concat(deploy_token_variables) @@ -596,7 +594,7 @@ module Ci def repo_url return unless token - auth = "gitlab-ci-token:#{token}@" + auth = "#{::Gitlab::Auth::CI_JOB_USER}:#{token}@" project.http_url_to_repo.sub(%r{^https?://}) do |prefix| prefix + auth end diff --git a/app/views/projects/packages/packages/show.html.haml b/app/views/projects/packages/packages/show.html.haml index 7a3d81c9124..fadaaadf8df 100644 --- a/app/views/projects/packages/packages/show.html.haml +++ b/app/views/projects/packages/packages/show.html.haml @@ -19,4 +19,6 @@ nuget_help_path: help_page_path('user/packages/nuget_repository/index'), pypi_path: pypi_registry_url(@project.id), pypi_setup_path: package_registry_project_url(@project.id, :pypi), - pypi_help_path: help_page_path('user/packages/pypi_repository/index') } } + pypi_help_path: help_page_path('user/packages/pypi_repository/index'), + project_name: @project.name, + one_column_view: Feature.enabled?(:packages_details_one_column, @project).to_s } } diff --git a/changelogs/unreleased/213918-document-elasticsearch-re-indexing-using-an-alias.yml b/changelogs/unreleased/213918-document-elasticsearch-re-indexing-using-an-alias.yml new file mode 100644 index 00000000000..6fd9c6b4a40 --- /dev/null +++ b/changelogs/unreleased/213918-document-elasticsearch-re-indexing-using-an-alias.yml @@ -0,0 +1,5 @@ +--- +title: Improve Elasticsearch Reindexing documentation +merge_request: 29788 +author: +type: other diff --git a/changelogs/unreleased/bvl-remove-redis-metrics-per-web-transaction.yml b/changelogs/unreleased/bvl-remove-redis-metrics-per-web-transaction.yml new file mode 100644 index 00000000000..675d4d55302 --- /dev/null +++ b/changelogs/unreleased/bvl-remove-redis-metrics-per-web-transaction.yml @@ -0,0 +1,5 @@ +--- +title: Remove per-web-transaction redis metrics +merge_request: 38101 +author: +type: other diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb index 93f0f25d84b..8e31e4f9282 100644 --- a/config/initializers/zz_metrics.rb +++ b/config/initializers/zz_metrics.rb @@ -147,7 +147,6 @@ if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && d Gitlab::Application.configure do |config| config.middleware.use(Gitlab::Metrics::RackMiddleware) config.middleware.use(Gitlab::Middleware::RailsQueueDuration) - config.middleware.use(Gitlab::Metrics::RedisRackMiddleware) config.middleware.use(Gitlab::Metrics::ElasticsearchRackMiddleware) end diff --git a/doc/administration/file_hooks.md b/doc/administration/file_hooks.md index 7903da675fd..c0b31769e7f 100644 --- a/doc/administration/file_hooks.md +++ b/doc/administration/file_hooks.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference +--- + # File hooks > - Introduced in GitLab 10.6. diff --git a/doc/administration/git_annex.md b/doc/administration/git_annex.md index 193478bfcdd..59c1e621e46 100644 --- a/doc/administration/git_annex.md +++ b/doc/administration/git_annex.md @@ -1,4 +1,8 @@ --- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, howto disqus_identifier: 'https://docs.gitlab.com/ee/workflow/git_annex.html' --- diff --git a/doc/administration/git_protocol.md b/doc/administration/git_protocol.md index e1600d972bd..841e636bd0a 100644 --- a/doc/administration/git_protocol.md +++ b/doc/administration/git_protocol.md @@ -1,4 +1,8 @@ --- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference description: "Set and configure Git protocol v2" --- diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md index 2a30eced7c4..49ea59d239c 100644 --- a/doc/administration/integration/plantuml.md +++ b/doc/administration/integration/plantuml.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, howto +--- + # PlantUML & GitLab > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8537) in GitLab 8.16. diff --git a/doc/administration/invalidate_markdown_cache.md b/doc/administration/invalidate_markdown_cache.md index 5fd804e11dc..ac43d0eae63 100644 --- a/doc/administration/invalidate_markdown_cache.md +++ b/doc/administration/invalidate_markdown_cache.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference +--- + # Invalidate Markdown Cache For performance reasons, GitLab caches the HTML version of Markdown text diff --git a/doc/administration/lfs/index.md b/doc/administration/lfs/index.md index 4a8151bd091..8b105d3d259 100644 --- a/doc/administration/lfs/index.md +++ b/doc/administration/lfs/index.md @@ -1,4 +1,8 @@ --- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, howto disqus_identifier: 'https://docs.gitlab.com/ee/workflow/lfs/lfs_administration.html' --- diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md index 93cdbff6621..3f4cd6e2751 100644 --- a/doc/administration/merge_request_diffs.md +++ b/doc/administration/merge_request_diffs.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Editor +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference +--- + # Merge request diffs storage **(CORE ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52568) in GitLab 11.8. diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index 9cf00216a1a..673ea20e7bf 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -97,8 +97,6 @@ The following metrics are available: | `gitlab_transaction_db_count_total` | Counter | 13.1 | Counter for total number of SQL calls | `controller`, `action` | | `gitlab_transaction_db_write_count_total` | Counter | 13.1 | Counter for total number of write SQL calls | `controller`, `action` | | `gitlab_transaction_db_cached_count_total` | Counter | 13.1 | Counter for total number of cached SQL calls | `controller`, `action` | -| `http_redis_requests_duration_seconds` | Histogram | 13.1 | Redis requests duration during web transactions | `controller`, `action` | -| `http_redis_requests_total` | Counter | 13.1 | Redis requests count during web transactions | `controller`, `action` | | `http_elasticsearch_requests_duration_seconds` **(STARTER)** | Histogram | 13.1 | Elasticsearch requests duration during web transactions | `controller`, `action` | | `http_elasticsearch_requests_total` **(STARTER)** | Counter | 13.1 | Elasticsearch requests count during web transactions | `controller`, `action` | | `pipelines_created_total` | Counter | 9.4 | Counter of pipelines created | | diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index 2751491accd..f00703ee507 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -97,7 +97,7 @@ This is why you will need: When using default setup, minimum configuration requires: -- `CONSUL_USERNAME`. Defaults to `gitlab-consul` +- `CONSUL_USERNAME`. The default user for Omnibus GitLab is `gitlab-consul` - `CONSUL_DATABASE_PASSWORD`. Password for the database user. - `CONSUL_PASSWORD_HASH`. This is a hash generated out of Consul username/password pair. Can be generated with: @@ -140,7 +140,7 @@ server nodes. We will need the following password information for the application's database user: -- `POSTGRESQL_USERNAME`. Defaults to `gitlab` +- `POSTGRESQL_USERNAME`. The default user for Omnibus GitLab is `gitlab` - `POSTGRESQL_USER_PASSWORD`. The password for the database user - `POSTGRESQL_PASSWORD_HASH`. This is a hash generated out of the username/password pair. Can be generated with: @@ -153,7 +153,7 @@ We will need the following password information for the application's database u When using default setup, minimum configuration requires: -- `PGBOUNCER_USERNAME`. Defaults to `pgbouncer` +- `PGBOUNCER_USERNAME`. The default user for Omnibus GitLab is `pgbouncer` - `PGBOUNCER_PASSWORD`. This is a password for PgBouncer service. - `PGBOUNCER_PASSWORD_HASH`. This is a hash generated out of PgBouncer username/password pair. Can be generated with: diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index 6d9ab723d2f..7d79840b56d 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Editor +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference +--- + # Repository checks > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/3232) in GitLab 8.7. diff --git a/doc/administration/snippets/index.md b/doc/administration/snippets/index.md index 1e4679d3a1d..95de3b8c183 100644 --- a/doc/administration/snippets/index.md +++ b/doc/administration/snippets/index.md @@ -2,7 +2,7 @@ type: reference, howto stage: Create group: Editor -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" --- # Snippets settings **(CORE ONLY)** diff --git a/doc/administration/static_objects_external_storage.md b/doc/administration/static_objects_external_storage.md index e802cb31639..34c7c8947fc 100644 --- a/doc/administration/static_objects_external_storage.md +++ b/doc/administration/static_objects_external_storage.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Editor +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference +--- + # Static objects external storage > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31025) in GitLab 12.3. diff --git a/doc/api/access_requests.md b/doc/api/access_requests.md index bcde2d4e041..c133a362788 100644 --- a/doc/api/access_requests.md +++ b/doc/api/access_requests.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Group and project access requests API > Introduced in GitLab 8.11. diff --git a/doc/api/branches.md b/doc/api/branches.md index 7a64f62189e..fbb5368cabc 100644 --- a/doc/api/branches.md +++ b/doc/api/branches.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Branches API This API operates on [repository branches](../user/project/repository/branches/index.md). diff --git a/doc/api/commits.md b/doc/api/commits.md index 9be4ce4fcdb..414ad46c9f6 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Commits API ## List repository commits diff --git a/doc/api/discussions.md b/doc/api/discussions.md index aa1a691a8f8..b9feef843b1 100644 --- a/doc/api/discussions.md +++ b/doc/api/discussions.md @@ -1,7 +1,8 @@ --- -stage: Plan -group: Project Management +stage: Create +group: Source Code info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +type: reference, api --- # Discussions API diff --git a/doc/api/group_wikis.md b/doc/api/group_wikis.md index 62094ffc940..414c795e092 100644 --- a/doc/api/group_wikis.md +++ b/doc/api/group_wikis.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Knowledge +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Wikis API > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/212199) in GitLab 13.2. diff --git a/doc/api/keys.md b/doc/api/keys.md index 6294ac300ce..b9e45c23e77 100644 --- a/doc/api/keys.md +++ b/doc/api/keys.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Keys API ## Get SSH key with user by ID of an SSH key diff --git a/doc/api/markdown.md b/doc/api/markdown.md index 32810ee349e..4e5c8515126 100644 --- a/doc/api/markdown.md +++ b/doc/api/markdown.md @@ -1,7 +1,8 @@ --- -stage: Plan -group: Project Management -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api --- # Markdown API diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md index 42fdfd1c7e6..643d03b6fb8 100644 --- a/doc/api/merge_request_approvals.md +++ b/doc/api/merge_request_approvals.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Merge request approvals API **(STARTER)** Configuration for approvals on all Merge Requests (MR) in the project. Must be authenticated for all endpoints. diff --git a/doc/api/merge_request_context_commits.md b/doc/api/merge_request_context_commits.md index e9ba31401ef..9b4697390d1 100644 --- a/doc/api/merge_request_context_commits.md +++ b/doc/api/merge_request_context_commits.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Merge request context commits API ## List MR context commits diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 7e98e1a71b3..4798145e837 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Merge requests API Every API call to merge requests must be authenticated. diff --git a/doc/api/project_aliases.md b/doc/api/project_aliases.md index d80decfe53c..cfd225639f9 100644 --- a/doc/api/project_aliases.md +++ b/doc/api/project_aliases.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Project Aliases API **(PREMIUM ONLY)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3264) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.1. diff --git a/doc/api/project_badges.md b/doc/api/project_badges.md index 86b1ba6ce19..936bd40d1ee 100644 --- a/doc/api/project_badges.md +++ b/doc/api/project_badges.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Project badges API > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17082) in GitLab 10.6. diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md index 74902a22594..5565eaa97f7 100644 --- a/doc/api/project_import_export.md +++ b/doc/api/project_import_export.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Project import/export API > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41899) in GitLab 10.6. diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md index 407e506e082..e8d4641bf6a 100644 --- a/doc/api/project_level_variables.md +++ b/doc/api/project_level_variables.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Project-level Variables API ## List project variables diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md index fd8cbd6e256..eccc8b4212d 100644 --- a/doc/api/project_snippets.md +++ b/doc/api/project_snippets.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Editor +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Project snippets ## Snippet visibility level diff --git a/doc/api/project_statistics.md b/doc/api/project_statistics.md index d96d3de6a73..344aeaa588f 100644 --- a/doc/api/project_statistics.md +++ b/doc/api/project_statistics.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Project statistics API Every API call to [project](../user/project/index.md) statistics must be authenticated. diff --git a/doc/api/project_templates.md b/doc/api/project_templates.md index e9658423b63..9e1bb78a854 100644 --- a/doc/api/project_templates.md +++ b/doc/api/project_templates.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Project templates API This API is a project-specific version of these endpoints: diff --git a/doc/api/project_vulnerabilities.md b/doc/api/project_vulnerabilities.md index c4f1adccd3c..8ab02ed9ea6 100644 --- a/doc/api/project_vulnerabilities.md +++ b/doc/api/project_vulnerabilities.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Project Vulnerabilities API **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10242) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6. diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md index 82d47f098f7..1f4f1eb933a 100644 --- a/doc/api/protected_branches.md +++ b/doc/api/protected_branches.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Protected branches API > Introduced in GitLab 9.5. diff --git a/doc/api/protected_tags.md b/doc/api/protected_tags.md index 658bec754fb..9f9c1ad8b5d 100644 --- a/doc/api/protected_tags.md +++ b/doc/api/protected_tags.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Protected tags API > Introduced in GitLab 11.3. diff --git a/doc/api/remote_mirrors.md b/doc/api/remote_mirrors.md index 6495f6d8383..a8355fb9009 100644 --- a/doc/api/remote_mirrors.md +++ b/doc/api/remote_mirrors.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Project remote mirrors API [Push mirrors](../user/project/repository/repository_mirroring.md#pushing-to-a-remote-repository-core) diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 7e601ec96ec..79ccd5bc9e4 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Repositories API ## List repository tree diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md index 9d934a0f855..cc1f0aa970c 100644 --- a/doc/api/repository_files.md +++ b/doc/api/repository_files.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Repository files API **CRUD for repository files** diff --git a/doc/api/repository_submodules.md b/doc/api/repository_submodules.md index 40708f5bcb0..9a5dcacbc2f 100644 --- a/doc/api/repository_submodules.md +++ b/doc/api/repository_submodules.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Repository submodules API > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41213) in GitLab 11.5 diff --git a/doc/api/search.md b/doc/api/search.md index 57316fa4fc2..4c87a826ca8 100644 --- a/doc/api/search.md +++ b/doc/api/search.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Search API > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41763) in GitLab 10.5. diff --git a/doc/api/snippets.md b/doc/api/snippets.md index db94716c2d4..0cdc07b1f46 100644 --- a/doc/api/snippets.md +++ b/doc/api/snippets.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Editor +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Snippets API > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6373) in GitLab 8.15. diff --git a/doc/api/suggestions.md b/doc/api/suggestions.md index e3bbcaa51c3..10528fe33b9 100644 --- a/doc/api/suggestions.md +++ b/doc/api/suggestions.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Suggest Changes API Every API call to suggestions must be authenticated. diff --git a/doc/api/tags.md b/doc/api/tags.md index 569271d6206..cf6cfd25dbb 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Tags API ## List project repository tags diff --git a/doc/api/visual_review_discussions.md b/doc/api/visual_review_discussions.md index bcbfdbdc6d0..12a4c20539c 100644 --- a/doc/api/visual_review_discussions.md +++ b/doc/api/visual_review_discussions.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Visual Review discussions API **(STARTER)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18710) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.5. diff --git a/doc/api/wikis.md b/doc/api/wikis.md index f7595e7efe6..7d16a5a38ee 100644 --- a/doc/api/wikis.md +++ b/doc/api/wikis.md @@ -1,3 +1,10 @@ +--- +stage: Create +group: Knowledge +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference, api +--- + # Wikis API > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13372) in GitLab 10.0. diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md index 90debab3b5c..8901eb0a3cd 100644 --- a/doc/development/elasticsearch.md +++ b/doc/development/elasticsearch.md @@ -121,6 +121,9 @@ Patterns: ## Zero downtime reindexing with multiple indices +NOTE: **Note:** +This is not applicable yet as multiple indices functionality is not fully implemented. + Currently GitLab can only handle a single version of setting. Any setting/schema changes would require reindexing everything from scratch. Since reindexing can take a long time, this can cause search functionality downtime. To avoid downtime, GitLab is working to support multiple indices that diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md index 551b372d3d9..8d4c8b3cf97 100644 --- a/doc/integration/elasticsearch.md +++ b/doc/integration/elasticsearch.md @@ -423,6 +423,140 @@ or creating [extra Sidekiq processes](../administration/operations/extra_sidekiq For repository and snippet files, GitLab will only index up to 1 MiB of content, in order to avoid indexing timeouts. +## Zero downtime reindexing + +The idea behind this reindexing method is to leverage Elasticsearch index alias feature to atomically swap between two indices. +We will refer to each index as `primary` (online and used by GitLab for read/writes) and `secondary` (offline, for reindexing purpose). + +Instead of connecting directly to the `primary` index, we'll setup an index alias such as we can change the underlying index at will. + +NOTE: **Note:** +Any index attached to the production alias is deemed a `primary` and will end up being used by the GitLab Elasticsearch integration. + +### Pause the indexing + +Under **Admin Area > Integration > Elasticsearch**, check the **Pause Elasticsearch Indexing** setting and save. + +With this, all updates that should happen on your Elasticsearch index will be buffered and caught up once unpaused. + +### Setup + +TIP: **Tip:** +If your index has been created with GitLab v13.0+ you can skip directly to [trigger the reindex](#trigger-the-reindex-via-the-elasticsearch-administration). + +This process involves multiple shell commands and curl invocations, so a good initial setup will help down the road: + +```shell +# You can find this value under Admin Area > Integration > Elasticsearch > URL +export CLUSTER_URL="http://localhost:9200" +export PRIMARY_INDEX="gitlab-production" +export SECONDARY_INDEX="gitlab-production-$(date +%s)" +``` + +### Reclaiming the `gitlab-production` index name + +CAUTION: **Caution:** +It is highly recommended that you take a snapshot of your cluster to make sure there is a recovery path if anything goes wrong. + +NOTE: **Note:** +Due to a technical limitation, there will be a slight downtime because of the fact that we need to reclaim the current `primary` index to be used as the alias. + +To reclaim the `gitlab-production` index name, you need to first create a `secondary` index and then trigger the re-index from `primary`. + +#### Creating a secondary index + +To create a secondary index, run the following Rake task. The `SKIP_ALIAS` +environment variable will disable the automatic creation of the Elasticsearch +alias, which would conflict with the existing index under `$PRIMARY_INDEX`: + +```shell +# Omnibus installation +sudo SKIP_ALIAS=1 gitlab-rake "gitlab:elastic:create_empty_index[$SECONDARY_INDEX]" + +# Source installation +SKIP_ALIAS=1 bundle exec rake "gitlab:elastic:create_empty_index[$SECONDARY_INDEX]" +``` + +The index should be created successfully, with the latest index options and mappings. + +#### Trigger the re-index from `primary` + +To trigger the re-index from `primary` index: + +1. Use the Elasticsearch [Reindex API](https://www.elastic.co/guide/en/elasticsearch/reference/7.6/docs-reindex.html): + + ```shell + curl --request POST \ + --header 'Content-Type: application/json' \ + --data "{ \"source\": { \"index\": \"$PRIMARY_INDEX\" }, \"dest\": { \"index\": \"$SECONDARY_INDEX\" } }" \ + "$CLUSTER_URL/_reindex?slices=auto&wait_for_completion=false" + ``` + + There will be an output like: + + ```plaintext + {"task":"3qw_Tr0YQLq7PF16Xek8YA:1012"} + ``` + + Note the `task` value here as it will be useful to follow the reindex progress. + +1. Wait for the reindex process to complete, by checking the `completed` value. + Using the `task` value form the previous step: + + ```shell + export TASK_ID=3qw_Tr0YQLq7PF16Xek8YA:1012 + curl "$CLUSTER_URL/_tasks/$TASK_ID?pretty" + ``` + + The output will be like: + + ```plaintext + {"completed":false, …} + ``` + + Once the returned value is `true`, you may continue to the next step. + +1. Make sure that the secondary index has data in it. You can use the Elasticsearch + API to look for the index size and compare our two indices: + + ```shell + curl $CLUSTER_URL/$PRIMARY_INDEX/_count => 123123 + curl $CLUSTER_URL/$SECONDARY_INDEX/_count => 123123 + ``` + + TIP: **Tip:** + Comparing the document count is more accurate than using the index size, as improvements to the storage might cause the new index to be smaller than the original one. + +1. Once you are confident your `secondary` index is valid, you can process to the creation of the alias. + + ```shell + # Delete the original index + curl --request DELETE $CLUSTER_URL/$PRIMARY_INDEX + + # Create the alias and add the `secondary` index to it + curl --request POST \ + --header 'Content-Type: application/json' \ + --data "{\"actions\":[{\"add\":{\"index\":\"$SECONDARY_INDEX\",\"alias\":\"$PRIMARY_INDEX\"}}]}}" \ + $CLUSTER_URL/_aliases + ``` + + The reindexing is now completed. Your GitLab instance is now ready to use the [automated in-cluster reindexing](#trigger-the-reindex-via-the-elasticsearch-administration) feature for future reindexing. + +1. Unpause the indexing + + Under **Admin Area > Integration > Elasticsearch**, uncheck the **Pause Elasticsearch Indexing** setting and save. + +### Trigger the reindex via the Elasticsearch administration + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34069) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.2. + +Under **Admin Area > Integration > Elasticsearch zero-downtime reindexing**, click on **Trigger cluster reindexing**. + +NOTE: **Note:** +Reindexing can be a lengthy process depending on the size of your Elasticsearch cluster. + +While the reindexing is running, you will be able to follow its progress under that same section. + ## GitLab Elasticsearch Rake tasks Rake tasks are available to: @@ -586,7 +720,7 @@ Here are some common pitfalls and how to overcome them: - **I indexed all the repositories but then switched Elasticsearch servers and now I can't find anything** - You will need to re-run all the Rake tasks to re-index the database, repositories, and wikis. + You will need to re-run all the Rake tasks to reindex the database, repositories, and wikis. - **The indexing process is taking a very long time** diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md index 787f1fe6575..0c2297d7f2c 100644 --- a/doc/user/admin_area/settings/account_and_limit_settings.md +++ b/doc/user/admin_area/settings/account_and_limit_settings.md @@ -1,4 +1,7 @@ --- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" type: reference --- diff --git a/doc/user/admin_area/settings/gitaly_timeouts.md b/doc/user/admin_area/settings/gitaly_timeouts.md index 68f359368f0..2003d02c9b3 100644 --- a/doc/user/admin_area/settings/gitaly_timeouts.md +++ b/doc/user/admin_area/settings/gitaly_timeouts.md @@ -1,4 +1,8 @@ --- +stage: Create +group: Gitaly +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" +type: reference type: reference --- diff --git a/doc/user/admin_area/settings/instance_template_repository.md b/doc/user/admin_area/settings/instance_template_repository.md index ae56c67f0ab..4a1945f7346 100644 --- a/doc/user/admin_area/settings/instance_template_repository.md +++ b/doc/user/admin_area/settings/instance_template_repository.md @@ -1,4 +1,7 @@ --- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" type: reference --- diff --git a/doc/user/admin_area/settings/push_event_activities_limit.md b/doc/user/admin_area/settings/push_event_activities_limit.md index bbc5ed04202..cf23ea12ef4 100644 --- a/doc/user/admin_area/settings/push_event_activities_limit.md +++ b/doc/user/admin_area/settings/push_event_activities_limit.md @@ -1,4 +1,7 @@ --- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" type: reference --- diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md index 92eeb6a04b7..82718f0d2e4 100644 --- a/doc/user/admin_area/settings/visibility_and_access_controls.md +++ b/doc/user/admin_area/settings/visibility_and_access_controls.md @@ -1,4 +1,7 @@ --- +stage: Create +group: Source Code +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers" type: reference --- diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md index bff56334831..ec2806cb439 100644 --- a/doc/user/project/web_ide/index.md +++ b/doc/user/project/web_ide/index.md @@ -31,7 +31,7 @@ file path fragments to start seeing results. ## Syntax highlighting -> Support for `.gitlab.ci.yml` validation [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218472) in GitLab 13.2. +> Support for `.gitlab-ci.yml` validation [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218472) in GitLab 13.2. As expected from an IDE, syntax highlighting for many languages within the Web IDE will make your direct editing even easier. diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 44e8c9c04b9..734d6f57c93 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -26,6 +26,9 @@ module Gitlab # Default scopes for OAuth applications that don't define their own DEFAULT_SCOPES = [:api].freeze + CI_JOB_USER = 'gitlab-ci-token' + CI_REGISTRY_USER = 'gitlab-ci-token' + class << self prepend_if_ee('EE::Gitlab::Auth') # rubocop: disable Cop/InjectEnterpriseEditionModule @@ -126,7 +129,7 @@ module Gitlab # rubocop:enable Gitlab/RailsLogger def skip_rate_limit?(login:) - ::Ci::Build::CI_REGISTRY_USER == login + CI_REGISTRY_USER == login end def look_to_limit_user(actor) @@ -254,7 +257,7 @@ module Gitlab end def build_access_token_check(login, password) - return unless login == 'gitlab-ci-token' + return unless login == CI_JOB_USER return unless password build = find_build_by_token(password) diff --git a/lib/gitlab/auth/auth_finders.rb b/lib/gitlab/auth/auth_finders.rb index ae38dc287c2..1f85b5a2e43 100644 --- a/lib/gitlab/auth/auth_finders.rb +++ b/lib/gitlab/auth/auth_finders.rb @@ -82,7 +82,7 @@ module Gitlab login, password = user_name_and_password(current_request) return unless login.present? && password.present? - return unless ::Ci::Build::CI_REGISTRY_USER == login + return unless ::Gitlab::Auth::CI_REGISTRY_USER == login job = ::Ci::Build.find_by_token(password) raise UnauthorizedError unless job diff --git a/lib/gitlab/metrics/redis_rack_middleware.rb b/lib/gitlab/metrics/redis_rack_middleware.rb deleted file mode 100644 index 9433355706c..00000000000 --- a/lib/gitlab/metrics/redis_rack_middleware.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Metrics - # Rack middleware for tracking Redis metrics from Grape and Web requests. - class RedisRackMiddleware - def initialize(app) - @app = app - end - - def call(env) - transaction = Gitlab::Metrics.current_transaction - - @app.call(env) - ensure - record_metrics(transaction) - end - - private - - def record_metrics(transaction) - query_time = Gitlab::Instrumentation::Redis.query_time - request_count = Gitlab::Instrumentation::Redis.get_request_count - - transaction.increment(:http_redis_requests_total, request_count) do - docstring 'Amount of calls to Redis servers during web requests' - end - - transaction.observe(:http_redis_requests_duration_seconds, query_time) do - docstring 'Query time for Redis servers during web requests' - buckets Gitlab::Instrumentation::Redis::QUERY_TIME_BUCKETS - end - end - end - end -end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 42a49d4afc4..63fb91c9be5 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -16774,12 +16774,21 @@ msgstr "" msgid "Package was removed" msgstr "" +msgid "PackageRegistry|%{name} version %{version} was created %{datetime}" +msgstr "" + +msgid "PackageRegistry|%{name} version %{version} was updated %{datetime}" +msgstr "" + msgid "PackageRegistry|Add Conan Remote" msgstr "" msgid "PackageRegistry|Add NuGet Source" msgstr "" +msgid "PackageRegistry|Commit %{link} on branch %{branch}" +msgstr "" + msgid "PackageRegistry|Composer" msgstr "" @@ -16897,6 +16906,12 @@ msgstr "" msgid "PackageRegistry|Pipeline %{linkStart}%{linkEnd} triggered %{timestamp} by %{author}" msgstr "" +msgid "PackageRegistry|Pipeline %{link} triggered %{datetime} by %{author}" +msgstr "" + +msgid "PackageRegistry|Published to the %{project} Package Registry %{datetime}" +msgstr "" + msgid "PackageRegistry|Published to the repository at %{timestamp}" msgstr "" diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 0849fb00e73..a41ff28841d 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -216,4 +216,23 @@ RSpec.describe SearchController do it_behaves_like 'when the user cannot read cross project', :autocomplete, { term: 'hello' } it_behaves_like 'with external authorization service enabled', :autocomplete, { term: 'hello' } end + + describe '#append_info_to_payload' do + it 'appends search metadata for logging' do + last_payload = nil + original_append_info_to_payload = controller.method(:append_info_to_payload) + + expect(controller).to receive(:append_info_to_payload) do |payload| + original_append_info_to_payload.call(payload) + last_payload = payload + end + + get :show, params: { scope: 'issues', search: 'hello world', group_id: '123', project_id: '456' } + + expect(last_payload[:metadata]['meta.search.group_id']).to eq('123') + expect(last_payload[:metadata]['meta.search.project_id']).to eq('456') + expect(last_payload[:metadata]['meta.search.search']).to eq('hello world') + expect(last_payload[:metadata]['meta.search.scope']).to eq('issues') + end + end end diff --git a/spec/frontend/boards/components/board_settings_sidebar_spec.js b/spec/frontend/boards/components/board_settings_sidebar_spec.js new file mode 100644 index 00000000000..50179e2b78b --- /dev/null +++ b/spec/frontend/boards/components/board_settings_sidebar_spec.js @@ -0,0 +1,237 @@ +import '~/boards/models/list'; +import MockAdapter from 'axios-mock-adapter'; +import axios from 'axios'; +import Vuex from 'vuex'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { GlDrawer, GlLabel, GlAvatarLink, GlAvatarLabeled } from '@gitlab/ui'; +import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue'; +import boardsStore from '~/boards/stores/boards_store'; +import sidebarEventHub from '~/sidebar/event_hub'; +import { inactiveId } from '~/boards/constants'; + +const localVue = createLocalVue(); + +localVue.use(Vuex); + +describe('BoardSettingsSidebar', () => { + let wrapper; + let mock; + let storeActions; + const labelTitle = 'test'; + const labelColor = '#FFFF'; + const listId = 1; + + const createComponent = (state = { activeId: inactiveId }, actions = {}) => { + storeActions = actions; + + const store = new Vuex.Store({ + state, + actions: storeActions, + }); + + wrapper = shallowMount(BoardSettingsSidebar, { + store, + localVue, + }); + }; + const findLabel = () => wrapper.find(GlLabel); + const findDrawer = () => wrapper.find(GlDrawer); + + beforeEach(() => { + boardsStore.create(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + wrapper.destroy(); + }); + + it('finds a GlDrawer component', () => { + createComponent(); + + expect(findDrawer().exists()).toBe(true); + }); + + describe('on close', () => { + it('calls closeSidebar', async () => { + const spy = jest.fn(); + createComponent({ activeId: inactiveId }, { setActiveId: spy }); + + findDrawer().vm.$emit('close'); + + await wrapper.vm.$nextTick(); + + expect(storeActions.setActiveId).toHaveBeenCalledWith( + expect.anything(), + inactiveId, + undefined, + ); + }); + + it('calls closeSidebar on sidebar.closeAll event', async () => { + createComponent({ activeId: inactiveId }, { setActiveId: jest.fn() }); + + sidebarEventHub.$emit('sidebar.closeAll'); + + await wrapper.vm.$nextTick(); + + expect(storeActions.setActiveId).toHaveBeenCalledWith( + expect.anything(), + inactiveId, + undefined, + ); + }); + }); + + describe('when activeId is zero', () => { + it('renders GlDrawer with open false', () => { + createComponent(); + + expect(findDrawer().props('open')).toBe(false); + }); + }); + + describe('when activeId is greater than zero', () => { + beforeEach(() => { + mock = new MockAdapter(axios); + + boardsStore.addList({ + id: listId, + label: { title: labelTitle, color: labelColor }, + list_type: 'label', + }); + }); + + afterEach(() => { + boardsStore.removeList(listId); + }); + + it('renders GlDrawer with open false', () => { + createComponent({ activeId: 1 }); + + expect(findDrawer().props('open')).toBe(true); + }); + }); + + describe('when activeId is in boardsStore', () => { + beforeEach(() => { + mock = new MockAdapter(axios); + + boardsStore.addList({ + id: listId, + label: { title: labelTitle, color: labelColor }, + list_type: 'label', + }); + + createComponent({ activeId: listId }); + }); + + afterEach(() => { + mock.restore(); + }); + + it('renders label title', () => { + expect(findLabel().props('title')).toBe(labelTitle); + }); + + it('renders label background color', () => { + expect(findLabel().props('backgroundColor')).toBe(labelColor); + }); + }); + + describe('when activeId is not in boardsStore', () => { + beforeEach(() => { + mock = new MockAdapter(axios); + + boardsStore.addList({ id: listId, label: { title: labelTitle, color: labelColor } }); + + createComponent({ activeId: inactiveId }); + }); + + afterEach(() => { + mock.restore(); + }); + + it('does not render GlLabel', () => { + expect(findLabel().exists()).toBe(false); + }); + }); + + describe('when activeList is present', () => { + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + boardsStore.removeList(listId); + }); + + describe('when list type is "milestone"', () => { + beforeEach(() => { + boardsStore.addList({ + id: 1, + milestone: { + webUrl: 'https://gitlab.com/h5bp/html5-boilerplate/-/milestones/1', + title: 'Backlog', + }, + max_issue_count: 1, + list_type: 'milestone', + }); + }); + + afterEach(() => { + boardsStore.removeList(1, 'milestone'); + wrapper.destroy(); + }); + + it('renders the correct milestone text', () => { + createComponent({ activeId: 1 }); + + expect(wrapper.find('.js-milestone').text()).toBe('Backlog'); + }); + + it('renders the correct list type text', () => { + createComponent({ activeId: 1 }); + + expect(wrapper.find('.js-list-label').text()).toBe('Milestone'); + }); + }); + + describe('when list type is "assignee"', () => { + beforeEach(() => { + boardsStore.addList({ + id: 1, + user: { username: 'root', avatar: '', name: 'Test', webUrl: 'https://gitlab.com/root' }, + max_issue_count: 1, + list_type: 'assignee', + }); + }); + + afterEach(() => { + boardsStore.removeList(1, 'assignee'); + wrapper.destroy(); + }); + + it('renders gl-avatar-link with correct href', () => { + createComponent({ activeId: 1 }); + + expect(wrapper.find(GlAvatarLink).exists()).toBe(true); + expect(wrapper.find(GlAvatarLink).attributes('href')).toBe('https://gitlab.com/root'); + }); + + it('renders gl-avatar-labeled with "root" as username and name as "Test"', () => { + createComponent({ activeId: 1 }); + + expect(wrapper.find(GlAvatarLabeled).exists()).toBe(true); + expect(wrapper.find(GlAvatarLabeled).attributes('label')).toBe('Test'); + expect(wrapper.find(GlAvatarLabeled).attributes('sublabel')).toBe('@root'); + }); + + it('renders the correct list type text', () => { + createComponent({ activeId: 1 }); + + expect(wrapper.find('.js-list-label').text()).toBe('Assignee'); + }); + }); + }); +}); diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js index 0debca1310a..b121e715b46 100644 --- a/spec/frontend/boards/stores/actions_spec.js +++ b/spec/frontend/boards/stores/actions_spec.js @@ -1,6 +1,7 @@ import actions from '~/boards/stores/actions'; import * as types from '~/boards/stores/mutation_types'; import testAction from 'helpers/vuex_action_helper'; +import { inactiveId } from '~/boards/constants'; const expectNotImplemented = action => { it('is not implemented', () => { @@ -25,6 +26,23 @@ describe('setEndpoints', () => { }); }); +describe('setActiveId', () => { + it('should commit mutation SET_ACTIVE_ID', done => { + const state = { + activeId: inactiveId, + }; + + testAction( + actions.setActiveId, + 1, + state, + [{ type: types.SET_ACTIVE_ID, payload: 1 }], + [], + done, + ); + }); +}); + describe('fetchLists', () => { expectNotImplemented(actions.fetchLists); }); diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js index bc57c30b354..61e552f34d0 100644 --- a/spec/frontend/boards/stores/mutations_spec.js +++ b/spec/frontend/boards/stores/mutations_spec.js @@ -32,6 +32,16 @@ describe('Board Store Mutations', () => { }); }); + describe('SET_ACTIVE_ID', () => { + it('updates aciveListId to be the value that is passed', () => { + const expectedId = 1; + + mutations.SET_ACTIVE_ID(state, expectedId); + + expect(state.activeId).toBe(expectedId); + }); + }); + describe('REQUEST_ADD_LIST', () => { expectNotImplemented(mutations.REQUEST_ADD_LIST); }); diff --git a/spec/frontend/design_management/components/upload/mock_data/all_versions.js b/spec/frontend/design_management/components/upload/mock_data/all_versions.js index e76bbd261bd..237e1654f9b 100644 --- a/spec/frontend/design_management/components/upload/mock_data/all_versions.js +++ b/spec/frontend/design_management/components/upload/mock_data/all_versions.js @@ -1,14 +1,10 @@ export default [ { - node: { - id: 'gid://gitlab/DesignManagement::Version/3', - sha: '0945756378e0b1588b9dd40d5a6b99e8b7198f55', - }, + id: 'gid://gitlab/DesignManagement::Version/3', + sha: '0945756378e0b1588b9dd40d5a6b99e8b7198f55', }, { - node: { - id: 'gid://gitlab/DesignManagement::Version/2', - sha: '5b063fef0cd7213b312db65b30e24f057df21b20', - }, + id: 'gid://gitlab/DesignManagement::Version/2', + sha: '5b063fef0cd7213b312db65b30e24f057df21b20', }, ]; diff --git a/spec/frontend/design_management/mock_data/all_versions.js b/spec/frontend/design_management/mock_data/all_versions.js index c389fdb8747..2b216574e27 100644 --- a/spec/frontend/design_management/mock_data/all_versions.js +++ b/spec/frontend/design_management/mock_data/all_versions.js @@ -1,8 +1,6 @@ export default [ { - node: { - id: 'gid://gitlab/DesignManagement::Version/1', - sha: 'b389071a06c153509e11da1f582005b316667001', - }, + id: 'gid://gitlab/DesignManagement::Version/1', + sha: 'b389071a06c153509e11da1f582005b316667001', }, ]; diff --git a/spec/frontend/design_management/mock_data/design.js b/spec/frontend/design_management/mock_data/design.js index 675198b9408..72be33fef1d 100644 --- a/spec/frontend/design_management/mock_data/design.js +++ b/spec/frontend/design_management/mock_data/design.js @@ -12,14 +12,12 @@ export default { webPath: 'full-issue-path', webUrl: 'full-issue-url', participants: { - edges: [ + nodes: [ { - node: { - name: 'Administrator', - username: 'root', - webUrl: 'link-to-author', - avatarUrl: 'link-to-avatar', - }, + name: 'Administrator', + username: 'root', + webUrl: 'link-to-author', + avatarUrl: 'link-to-avatar', }, ], }, diff --git a/spec/frontend/design_management/mock_data/designs.js b/spec/frontend/design_management/mock_data/designs.js index 07f5c1b7457..98a24081ae6 100644 --- a/spec/frontend/design_management/mock_data/designs.js +++ b/spec/frontend/design_management/mock_data/designs.js @@ -5,11 +5,7 @@ export default { issue: { designCollection: { designs: { - edges: [ - { - node: design, - }, - ], + nodes: [design], }, }, }, diff --git a/spec/frontend/design_management/mock_data/no_designs.js b/spec/frontend/design_management/mock_data/no_designs.js index 9db0ffcade2..0ccb83492fc 100644 --- a/spec/frontend/design_management/mock_data/no_designs.js +++ b/spec/frontend/design_management/mock_data/no_designs.js @@ -3,7 +3,7 @@ export default { issue: { designCollection: { designs: { - edges: [], + nodes: [], }, }, }, diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js index 74f148e5ce0..69f757a6f88 100644 --- a/spec/frontend/design_management/pages/index_spec.js +++ b/spec/frontend/design_management/pages/index_spec.js @@ -57,9 +57,7 @@ const mockDesigns = [ ]; const mockVersion = { - node: { - id: 'gid://gitlab/DesignManagement::Version/1', - }, + id: 'gid://gitlab/DesignManagement::Version/1', }; describe('Design management index page', () => { @@ -240,13 +238,10 @@ describe('Design management index page', () => { }, versions: { __typename: 'DesignVersionConnection', - edges: { - __typename: 'DesignVersionEdge', - node: { - __typename: 'DesignVersion', - id: expect.anything(), - sha: expect.anything(), - }, + nodes: { + __typename: 'DesignVersion', + id: expect.anything(), + sha: expect.anything(), }, }, }, diff --git a/spec/frontend/design_management/utils/design_management_utils_spec.js b/spec/frontend/design_management/utils/design_management_utils_spec.js index 478ebadc8f6..e6d836b9157 100644 --- a/spec/frontend/design_management/utils/design_management_utils_spec.js +++ b/spec/frontend/design_management/utils/design_management_utils_spec.js @@ -51,7 +51,7 @@ describe('extractDiscussions', () => { }; }); - it('discards the edges.node artifacts of GraphQL', () => { + it('discards the node artifacts of GraphQL', () => { expect(extractDiscussions(discussions)).toEqual([ { id: 1, notes: ['a'], index: 1 }, { id: 2, notes: ['b'], index: 2 }, @@ -96,10 +96,7 @@ describe('optimistic responses', () => { discussions: { __typename: 'DesignDiscussion', nodes: [] }, versions: { __typename: 'DesignVersionConnection', - edges: { - __typename: 'DesignVersionEdge', - node: { __typename: 'DesignVersion', id: -1, sha: -1 }, - }, + nodes: { __typename: 'DesignVersion', id: -1, sha: -1 }, }, }, ], diff --git a/spec/frontend/packages/details/components/__snapshots__/activity_spec.js.snap b/spec/frontend/packages/details/components/__snapshots__/activity_spec.js.snap index 8590fd242b1..2e88ac39d18 100644 --- a/spec/frontend/packages/details/components/__snapshots__/activity_spec.js.snap +++ b/spec/frontend/packages/details/components/__snapshots__/activity_spec.js.snap @@ -62,14 +62,14 @@ exports[`PackageActivity render to match the default snapshot when there is a pi <!----> <gl-link-stub - href="../../commit/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + href="../../commit/sha-baz" > - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + sha-baz </gl-link-stub> <clipboard-button-stub cssclass="border-0 text-secondary py-0" - text="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + text="sha-baz" title="Copy commit SHA" tooltipplacement="top" /> diff --git a/spec/frontend/packages/details/components/__snapshots__/history_element_spec.js.snap b/spec/frontend/packages/details/components/__snapshots__/history_element_spec.js.snap new file mode 100644 index 00000000000..dff783a2b8d --- /dev/null +++ b/spec/frontend/packages/details/components/__snapshots__/history_element_spec.js.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`History Element renders the correct markup 1`] = ` +<li + class="timeline-entry system-note note-wrapper gl-my-6!" +> + <div + class="timeline-entry-inner" + > + <div + class="timeline-icon" + > + <gl-icon-stub + name="pencil" + size="16" + /> + </div> + + <div + class="timeline-content" + > + <div + class="note-header" + > + <span> + <div + data-testid="default-slot" + /> + </span> + </div> + + <div + class="note-body" + /> + </div> + </div> +</li> +`; diff --git a/spec/frontend/packages/details/components/app_spec.js b/spec/frontend/packages/details/components/app_spec.js index 9f72ef67816..a22588be39c 100644 --- a/spec/frontend/packages/details/components/app_spec.js +++ b/spec/frontend/packages/details/components/app_spec.js @@ -16,6 +16,8 @@ import ConanInstallation from '~/packages/details/components/conan_installation. import NugetInstallation from '~/packages/details/components/nuget_installation.vue'; import PypiInstallation from '~/packages/details/components/pypi_installation.vue'; import DependencyRow from '~/packages/details/components/dependency_row.vue'; +import PackageHistory from '~/packages/details/components/package_history.vue'; +import PackageActivity from '~/packages/details/components/activity.vue'; import { conanPackage, mavenPackage, @@ -39,6 +41,7 @@ describe('PackagesApp', () => { packageEntity = mavenPackage, packageFiles = mavenFiles, isLoading = false, + oneColumnView = false, } = {}) { store = new Vuex.Store({ state: { @@ -50,6 +53,8 @@ describe('PackagesApp', () => { emptySvgPath: 'empty-illustration', npmPath: 'foo', npmHelpPath: 'foo', + projectName: 'bar', + oneColumnView, }, actions: { fetchPackageVersions, @@ -93,6 +98,9 @@ describe('PackagesApp', () => { const dependenciesCountBadge = () => wrapper.find('[data-testid="dependencies-badge"]'); const noDependenciesMessage = () => wrapper.find('[data-testid="no-dependencies-message"]'); const dependencyRows = () => wrapper.findAll(DependencyRow); + const findPackageHistory = () => wrapper.find(PackageHistory); + const findPackageActivity = () => wrapper.find(PackageActivity); + const findOldPackageInfo = () => wrapper.find('[data-testid="old-package-info"]'); afterEach(() => { wrapper.destroy(); @@ -286,4 +294,31 @@ describe('PackagesApp', () => { ); }); }); + + describe('one column layout feature flag', () => { + describe.each` + oneColumnView | history | oldInfo | activity + ${true} | ${true} | ${false} | ${false} + ${false} | ${false} | ${true} | ${true} + `( + 'with oneColumnView set to $oneColumnView', + ({ oneColumnView, history, oldInfo, activity }) => { + beforeEach(() => { + createComponent({ oneColumnView }); + }); + + it('package history', () => { + expect(findPackageHistory().exists()).toBe(history); + }); + + it('old info block', () => { + expect(findOldPackageInfo().exists()).toBe(oldInfo); + }); + + it('package activity', () => { + expect(findPackageActivity().exists()).toBe(activity); + }); + }, + ); + }); }); diff --git a/spec/frontend/packages/details/components/history_element_spec.js b/spec/frontend/packages/details/components/history_element_spec.js new file mode 100644 index 00000000000..e8746fc93f5 --- /dev/null +++ b/spec/frontend/packages/details/components/history_element_spec.js @@ -0,0 +1,57 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlIcon } from '@gitlab/ui'; +import component from '~/packages/details/components/history_element.vue'; +import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; + +describe('History Element', () => { + let wrapper; + const defaultProps = { + icon: 'pencil', + }; + + const mountComponent = () => { + wrapper = shallowMount(component, { + propsData: { ...defaultProps }, + stubs: { + TimelineEntryItem, + }, + slots: { + default: '<div data-testid="default-slot"></div>', + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findTimelineEntry = () => wrapper.find(TimelineEntryItem); + const findGlIcon = () => wrapper.find(GlIcon); + const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]'); + + it('renders the correct markup', () => { + mountComponent(); + + expect(wrapper.element).toMatchSnapshot(); + }); + + it('has a default slot', () => { + mountComponent(); + + expect(findDefaultSlot().exists()).toBe(true); + }); + it('has a timeline entry', () => { + mountComponent(); + + expect(findTimelineEntry().exists()).toBe(true); + }); + it('has an icon', () => { + mountComponent(); + + const icon = findGlIcon(); + + expect(icon.exists()).toBe(true); + expect(icon.attributes('name')).toBe(defaultProps.icon); + }); +}); diff --git a/spec/frontend/packages/details/components/package_history_spec.js b/spec/frontend/packages/details/components/package_history_spec.js new file mode 100644 index 00000000000..c4d8163abf4 --- /dev/null +++ b/spec/frontend/packages/details/components/package_history_spec.js @@ -0,0 +1,106 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlLink, GlSprintf } from '@gitlab/ui'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import component from '~/packages/details/components/package_history.vue'; + +import { mavenPackage, mockPipelineInfo } from '../../mock_data'; + +describe('Package History', () => { + let wrapper; + const defaultProps = { + projectName: 'baz project', + packageEntity: { ...mavenPackage }, + }; + + const mountComponent = props => { + wrapper = shallowMount(component, { + propsData: { ...defaultProps, ...props }, + stubs: { + HistoryElement: '<div data-testid="history-element"><slot></slot></div>', + GlSprintf, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findHistoryElement = testId => wrapper.find(`[data-testid="${testId}"]`); + const findElementLink = container => container.find(GlLink); + const findElementTimeAgo = container => container.find(TimeAgoTooltip); + const findTitle = () => wrapper.find('[data-testid="title"]'); + const findTimeline = () => wrapper.find('[data-testid="timeline"]'); + + it('has the correct title', () => { + mountComponent(); + + const title = findTitle(); + + expect(title.exists()).toBe(true); + expect(title.text()).toBe('History'); + }); + + it('has a timeline container', () => { + mountComponent(); + + const title = findTimeline(); + + expect(title.exists()).toBe(true); + expect(title.classes()).toEqual( + expect.arrayContaining(['timeline', 'main-notes-list', 'notes']), + ); + }); + + describe.each` + name | icon | text | timeAgoTooltip | link + ${'created-on'} | ${'clock'} | ${'Test package version 1.0.0 was created'} | ${mavenPackage.created_at} | ${null} + ${'updated-at'} | ${'pencil'} | ${'Test package version 1.0.0 was updated'} | ${mavenPackage.updated_at} | ${null} + ${'commit'} | ${'commit'} | ${'Commit sha-baz on branch branch-name'} | ${null} | ${'../../commit/sha-baz'} + ${'pipeline'} | ${'pipeline'} | ${'Pipeline #1 triggered by foo'} | ${mockPipelineInfo.created_at} | ${'../../pipelines/1'} + ${'published'} | ${'package'} | ${'Published to the baz project Package Registry'} | ${mavenPackage.created_at} | ${null} + `('history element $name', ({ name, icon, text, timeAgoTooltip, link }) => { + let element; + + beforeEach(() => { + mountComponent({ packageEntity: { ...mavenPackage, pipeline: mockPipelineInfo } }); + element = findHistoryElement(name); + }); + + it('has the correct icon', () => { + expect(element.props('icon')).toBe(icon); + }); + + it('has the correct text', () => { + expect(element.text()).toBe(text); + }); + + it('time-ago tooltip', () => { + const timeAgo = findElementTimeAgo(element); + const exist = Boolean(timeAgoTooltip); + + expect(timeAgo.exists()).toBe(exist); + if (exist) { + expect(timeAgo.props('time')).toBe(timeAgoTooltip); + } + }); + + it('link', () => { + const linkElement = findElementLink(element); + const exist = Boolean(link); + + expect(linkElement.exists()).toBe(exist); + if (exist) { + expect(linkElement.attributes('href')).toBe(link); + } + }); + }); + + describe('when pipelineInfo is missing', () => { + it.each(['commit', 'pipeline'])('%s history element is hidden', name => { + mountComponent(); + expect(findHistoryElement(name).exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/packages/mock_data.js b/spec/frontend/packages/mock_data.js index 62cf73d44ac..1ca81ef3d3b 100644 --- a/spec/frontend/packages/mock_data.js +++ b/spec/frontend/packages/mock_data.js @@ -6,7 +6,7 @@ const _links = { export const mockPipelineInfo = { id: 1, ref: 'branch-name', - sha: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + sha: 'sha-baz', user: { name: 'foo', }, @@ -14,6 +14,7 @@ export const mockPipelineInfo = { name: 'foo-project', web_url: 'foo-project-link', }, + created_at: '2015-12-10', }; export const mavenPackage = { diff --git a/spec/frontend/packages/shared/components/__snapshots__/publish_method_spec.js.snap b/spec/frontend/packages/shared/components/__snapshots__/publish_method_spec.js.snap index 01a2e80173a..5ecca63d41d 100644 --- a/spec/frontend/packages/shared/components/__snapshots__/publish_method_spec.js.snap +++ b/spec/frontend/packages/shared/components/__snapshots__/publish_method_spec.js.snap @@ -24,14 +24,14 @@ exports[`publish_method renders 1`] = ` <gl-link-stub class="mr-1" - href="../commit/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + href="../commit/sha-baz" > - xxxxxxxx + sha-baz </gl-link-stub> <clipboard-button-stub cssclass="border-0 text-secondary py-0 px-1" - text="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + text="sha-baz" title="Copy commit SHA" tooltipplacement="top" /> diff --git a/spec/frontend/repository/components/preview/index_spec.js b/spec/frontend/repository/components/preview/index_spec.js index 6ae323f5c3f..ebd985e640c 100644 --- a/spec/frontend/repository/components/preview/index_spec.js +++ b/spec/frontend/repository/components/preview/index_spec.js @@ -30,7 +30,7 @@ describe('Repository file preview component', () => { it('renders file HTML', () => { factory({ - webUrl: 'http://test.com', + webPath: 'http://test.com', name: 'README.md', }); @@ -43,7 +43,7 @@ describe('Repository file preview component', () => { it('handles hash after render', () => { factory({ - webUrl: 'http://test.com', + webPath: 'http://test.com', name: 'README.md', }); @@ -59,7 +59,7 @@ describe('Repository file preview component', () => { it('renders loading icon', () => { factory({ - webUrl: 'http://test.com', + webPath: 'http://test.com', name: 'README.md', }); diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb index 7ec4bb65613..d56c9395fb9 100644 --- a/spec/lib/gitlab/auth/auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/auth_finders_spec.rb @@ -554,7 +554,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do end context 'with CI username' do - let(:username) { ::Ci::Build::CI_REGISTRY_USER } + let(:username) { ::Gitlab::Auth::CI_REGISTRY_USER } let(:user) { create(:user) } let(:build) { create(:ci_build, user: user) } @@ -727,7 +727,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do context 'when the job token is provided via basic auth' do let(:route_authentication_setting) { { job_token_allowed: :basic_auth } } - let(:username) { Ci::Build::CI_REGISTRY_USER } + let(:username) { ::Gitlab::Auth::CI_REGISTRY_USER } let(:token) { job.token } before do diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index 0b391c8cba9..a73c6317f31 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -149,7 +149,9 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do end context 'build token' do - subject { gl_auth.find_for_git_client('gitlab-ci-token', build.token, project: project, ip: 'ip') } + subject { gl_auth.find_for_git_client(username, build.token, project: project, ip: 'ip') } + + let(:username) { 'gitlab-ci-token' } context 'for running build' do let!(:build) { create(:ci_build, :running) } @@ -170,6 +172,14 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do expect(subject).to eq(Gitlab::Auth::Result.new(nil, nil, nil, nil)) end + + context 'username is not gitlab-ci-token' do + let(:username) { 'another_username' } + + it 'fails to authenticate' do + expect(subject).to eq(Gitlab::Auth::Result.new(nil, nil, nil, nil)) + end + end end (Ci::HasStatus::AVAILABLE_STATUSES - ['running']).each do |build_status| diff --git a/spec/lib/gitlab/metrics/redis_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/redis_rack_middleware_spec.rb deleted file mode 100644 index 735731c9d2c..00000000000 --- a/spec/lib/gitlab/metrics/redis_rack_middleware_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::Metrics::RedisRackMiddleware do - let(:app) { double(:app) } - let(:middleware) { described_class.new(app) } - let(:env) { {} } - let(:transaction) { Gitlab::Metrics::WebTransaction.new(env) } - - before do - allow(app).to receive(:call).with(env).and_return('wub wub') - end - - describe '#call' do - let(:redis_query_time) { 0.1 } - let(:redis_requests_count) { 2 } - - before do - allow(Gitlab::Instrumentation::Redis).to receive(:query_time) { redis_query_time } - allow(Gitlab::Instrumentation::Redis).to receive(:get_request_count) { redis_requests_count } - allow(Gitlab::Metrics).to receive(:current_transaction).and_return(transaction) - end - - it 'calls the app' do - expect(middleware.call(env)).to eq('wub wub') - end - - it 'records redis metrics' do - expect(transaction).to receive(:increment).with(:http_redis_requests_total, redis_requests_count) - expect(transaction).to receive(:observe).with(:http_redis_requests_duration_seconds, redis_query_time) - - middleware.call(env) - end - - it 'records redis metrics if an error is raised' do - expect(transaction).to receive(:increment).with(:http_redis_requests_total, redis_requests_count) - expect(transaction).to receive(:observe).with(:http_redis_requests_duration_seconds, redis_query_time) - - allow(app).to receive(:call).with(env).and_raise(StandardError) - - expect { middleware.call(env) }.to raise_error(StandardError) - end - end -end diff --git a/spec/support/helpers/http_basic_auth_helpers.rb b/spec/support/helpers/http_basic_auth_helpers.rb index c0b24b3dfa4..d0cdcc6dd66 100644 --- a/spec/support/helpers/http_basic_auth_helpers.rb +++ b/spec/support/helpers/http_basic_auth_helpers.rb @@ -8,7 +8,7 @@ module HttpBasicAuthHelpers end def job_basic_auth_header(job) - basic_auth_header(Ci::Build::CI_REGISTRY_USER, job.token) + basic_auth_header(::Gitlab::Auth::CI_REGISTRY_USER, job.token) end def client_basic_auth_header(client) diff --git a/spec/support/shared_examples/features/packages_shared_examples.rb b/spec/support/shared_examples/features/packages_shared_examples.rb index 412a5ee4881..b6b6f90c72a 100644 --- a/spec/support/shared_examples/features/packages_shared_examples.rb +++ b/spec/support/shared_examples/features/packages_shared_examples.rb @@ -21,6 +21,10 @@ end RSpec.shared_examples 'package details link' do |property| let(:package) { packages.first } + before do + stub_feature_flags(packages_details_one_column: false) + end + it 'navigates to the correct url' do page.within(packages_table_selector) do click_link package.name |