diff options
author | Stan Hu <stanhu@gmail.com> | 2018-08-29 22:54:12 -0700 |
---|---|---|
committer | Stan Hu <stanhu@gmail.com> | 2018-08-29 22:54:12 -0700 |
commit | 69eddc14b11b63429b8f2511a1127616c692b94c (patch) | |
tree | a94482be144cef60a8ee1b590857ca24f49f418a | |
parent | bc7a4eedf9fa6681465b622af52c34d49ffb5d0e (diff) | |
parent | f981d4febbbb5103262f4daa858236d9c4ed9d67 (diff) | |
download | gitlab-ce-69eddc14b11b63429b8f2511a1127616c692b94c.tar.gz |
Merge branch 'master' into sh-test-ldap-clones-via-gitlab-qa
826 files changed, 4602 insertions, 1984 deletions
diff --git a/.flayignore b/.flayignore index 4b6f7ba693a..87411516a2a 100644 --- a/.flayignore +++ b/.flayignore @@ -6,6 +6,7 @@ app/controllers/projects/approvers_controller.rb app/controllers/projects/protected_branches/merge_access_levels_controller.rb app/controllers/projects/protected_branches/push_access_levels_controller.rb app/controllers/projects/protected_tags/create_access_levels_controller.rb +app/helpers/system_note_helper.rb app/policies/project_policy.rb app/models/concerns/relative_positioning.rb app/workers/stuck_merge_jobs_worker.rb diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 797a20ef16e..23d71675ae4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -131,7 +131,7 @@ stages: .single-script-job: &single-script-job image: ruby:2.4-alpine before_script: [] - stage: build + stage: test cache: {} dependencies: [] variables: &single-script-job-variables diff --git a/.gitlab/issue_templates/Test plan.md b/.gitlab/issue_templates/Test plan.md new file mode 100644 index 00000000000..580fab206b3 --- /dev/null +++ b/.gitlab/issue_templates/Test plan.md @@ -0,0 +1,96 @@ +# Test Plan + +<!-- This issue outlines testing activities related to a particular issue or epic. + +[Here is an example test plan](https://gitlab.com/gitlab-org/gitlab-ce/issues/50353) + +This and other comments should be removed as you write the plan --> + +## Introduction + +<!-- Briefly outline what is being tested + +Mention the issue(s) this test plan is related to --> + +## Scope + +<!-- State any limits on aspects of the feature being tested +Outline the types of data to be included +Outline the types of tests to be performed (functional, security, performance, +database, automated, etc) --> + +## ACC Matrix + +<!-- Use the matrix below as a template to identify the Attributes, Components, and +Capabilities relevant to the scope of this test plan. Add or remove Attributes +and Components as required and list Capabilities in the next section + +Attributes (columns) are adverbs or adjectives that describe (at a high level) +the qualities testing is meant to ensure Components have. + +Components (rows) are nouns that define major parts of the product being tested. + +Capabilities link Attributes and Components. They are what your product needs to +do to make sure a Component fulfills an Attribute + +For more information see the [Google Testing Blog article about the 10 minute +test plan](https://testing.googleblog.com/2011/09/10-minute-test-plan.html) and +[this wiki page from an open-source tool that implements the ACC +model](https://code.google.com/archive/p/test-analytics/wikis/AccExplained.wiki). --> + +| | Simple | Secure | Responsive | Obvious | Stable | +|------------|:------:|:------:|:----------:|:-------:|:------:| +| Admin | | | | | | +| Groups | | | | | | +| Project | | | | | | +| Repository | | | | | | +| Issues | | | | | | +| MRs | | | | | | +| CI/CD | | | | | | +| Ops | | | | | | +| Registry | | | | | | +| Wiki | | | | | | +| Snippets | | | | | | +| Settings | | | | | | +| Tracking | | | | | | +| API | | | | | | + +## Capabilities + +<!-- Use the ACC matrix above to help you identify Capabilities at each relevant +intersection of Components and Attributes. + +Some features might be simple enough that they only involve one Component, while +more complex features could involve multiple or even all. + +Example (from https://gitlab.com/gitlab-org/gitlab-ce/issues/50353): +* Respository is + * Simple + * It's easy to select the desired file template + * It doesn't require unnecessary actions to save the change + * It's easy to undo the change after selecting a template + * Responsive + * The list of templates can be restricted to allow a user to find a specific template among many + * Once a template is selected the file content updates quickly and smoothly +--> + +## Test Plan + +<!-- If the scope is small enough you may not need to write a list of tests to +perform. It might be enough to use the Capabilities to guide your testing. + +If the feature is more complex, especially if it involves multiple Components, +briefly outline a set of tests here. When identifying tests to perform be sure +to consider risk. Note inherent/known levels of risk so that testing can focus +on high risk areas first. + +New end-to-end and integration tests (Selenium and API) should be added to the +[Test Coverage sheet](https://docs.google.com/spreadsheets/d/1RlLfXGboJmNVIPP9jgFV5sXIACGfdcFq1tKd7xnlb74/) + +Please note if automated tests already exist. + +When adding new automated tests, please keep [testing levels](https://docs.gitlab.com/ce/development/testing_guide/testing_levels.html) +in mind. +--> + +/label ~Quality
\ No newline at end of file diff --git a/.haml-lint.yml b/.haml-lint.yml index 32c7de0fb78..fcdc47af60f 100644 --- a/.haml-lint.yml +++ b/.haml-lint.yml @@ -70,14 +70,15 @@ linters: enabled: false RuboCop: - enabled: false + enabled: true # These cops are incredibly noisy when it comes to HAML templates, so we # ignore them. ignored_cops: - - Lint/BlockAlignment - - Lint/EndAlignment + - Layout/BlockAlignment + - Layout/EndAlignment - Lint/Void - Metrics/LineLength + - Naming/FileName - Style/AlignParameters - Style/BlockNesting - Style/ElseAlignment @@ -91,6 +92,52 @@ linters: - Style/TrailingWhitespace - Style/WhileUntilModifier + # These cops should eventually get enabled + - Cop/LineBreakAfterGuardClauses + - Cop/LineBreakAroundConditionalBlock + - Cop/ProjectPathHelper + - GitlabSecurity/PublicSend + - Layout/LeadingCommentSpace + - Layout/SpaceAfterColon + - Layout/SpaceAfterComma + - Layout/SpaceAroundOperators + - Layout/SpaceBeforeBlockBraces + - Layout/SpaceBeforeComma + - Layout/SpaceBeforeFirstArg + - Layout/SpaceInsideArrayLiteralBrackets + - Layout/SpaceInsideHashLiteralBraces + - Layout/SpaceInsideStringInterpolation + - Layout/TrailingBlankLines + - Lint/BooleanSymbol + - Lint/LiteralInInterpolation + - Lint/ParenthesesAsGroupedExpression + - Lint/RedundantWithIndex + - Lint/Syntax + - Lint/UselessAssignment + - Metrics/BlockNesting + - Naming/VariableName + - Performance/RedundantMatch + - Performance/StringReplacement + - Rails/Presence + - Rails/RequestReferer + - Style/AndOr + - Style/ColonMethodCall + - Style/ConditionalAssignment + - Style/HashSyntax + - Style/IdenticalConditionalBranches + - Style/NegatedIf + - Style/NestedTernaryOperator + - Style/Not + - Style/ParenthesesAroundCondition + - Style/RedundantParentheses + - Style/SelfAssignment + - Style/Semicolon + - Style/TernaryParentheses + - Style/TrailingCommaInHashLiteral + - Style/UnlessElse + - Style/WordArray + - Style/ZeroLengthPredicate + RubyComments: enabled: true diff --git a/CHANGELOG.md b/CHANGELOG.md index aee36bb6df3..5e022b7e52b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 11.2.3 (2018-08-28) + +- No changes. + +## 11.2.2 (2018-08-27) + +### Security (3 changes) + +- Fixed persistent XSS rendering/escaping of diff location lines. +- Adding CSRF protection to Hooks resend action. +- Block link-local addresses in URLBlocker. + + +## 11.2.1 (2018-08-22) + +### Fixed (2 changes) + +- Fix wrong commit count in push event payload. !21338 +- Fix broken Git over HTTP clones with LDAP users. !21352 + +### Performance (1 change) + +- Eliminate unnecessary and duplicate system hook fires. !21337 + + ## 11.2.0 (2018-08-22) ### Security (5 changes) @@ -242,6 +267,23 @@ entry. - Moves help_popover component to a common location. +## 11.1.6 (2018-08-28) + +- No changes. + +## 11.1.5 (2018-08-27) + +### Security (3 changes) + +- Fixed persistent XSS rendering/escaping of diff location lines. +- Adding CSRF protection to Hooks resend action. +- Block link-local addresses in URLBlocker. + +### Fixed (1 change, 1 of them is from the community) + +- Sanitize git URL in import errors. (Jamie Schembri) + + ## 11.1.4 (2018-07-30) ### Fixed (4 changes, 1 of them is from the community) @@ -524,6 +566,19 @@ entry. - Use monospaced font for MR diff commit link ref on GFM. +## 11.0.6 (2018-08-27) + +### Security (3 changes) + +- Fixed persistent XSS rendering/escaping of diff location lines. +- Adding CSRF protection to Hooks resend action. +- Block link-local addresses in URLBlocker. + +### Fixed (1 change, 1 of them is from the community) + +- Sanitize git URL in import errors. (Jamie Schembri) + + ## 11.0.5 (2018-07-26) ### Security (4 changes) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb7c0c88629..1b9e9d4a5a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -314,7 +314,7 @@ This [documentation](doc/development/contributing/merge_request_workflow.md) has ## Definition of done -This [documentation](doc/development/contributing/merge_request_workflow.md)) has been moved. +This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved. ## Style guides diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index f9ec29cde90..63b450d3f62 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -126,7 +126,7 @@ GEM numerizer (~> 0.1.1) chunky_png (1.3.5) citrus (3.0.2) - coderay (1.1.1) + coderay (1.1.2) coercible (1.0.0) descendants_tracker (~> 0.0.1) commonmarker (0.17.8) @@ -495,7 +495,7 @@ GEM memoist (0.16.0) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) - method_source (0.8.2) + method_source (0.9.0) mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) @@ -638,12 +638,11 @@ GEM unparser procto (0.0.3) prometheus-client-mmap (0.9.4) - pry (0.10.4) + pry (0.11.3) coderay (~> 1.1.0) - method_source (~> 0.8.1) - slop (~> 3.4) - pry-byebug (3.4.2) - byebug (~> 9.0) + method_source (~> 0.9.0) + pry-byebug (3.4.3) + byebug (>= 9.0, < 9.1) pry (~> 0.10) pry-rails (0.3.5) pry (>= 0.9.10) @@ -751,7 +750,7 @@ GEM retriable (3.1.2) rinku (2.0.0) rotp (2.1.2) - rouge (3.2.0) + rouge (3.2.1) rqrcode (0.7.0) chunky_png rqrcode-rails3 (0.1.7) @@ -817,7 +816,7 @@ GEM rubyzip (1.2.1) rufus-scheduler (3.4.0) et-orbi (~> 1.0) - rugged (0.27.2) + rugged (0.27.4) safe_yaml (1.0.4) sanitize (4.6.6) crass (~> 1.0.2) @@ -878,7 +877,6 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.0) slack-notifier (1.5.1) - slop (3.6.0) spring (2.0.1) activesupport (>= 4.2) spring-commands-rspec (1.0.4) diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index e34db893989..5b0c4285339 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -109,8 +109,6 @@ export class AwardsHandler { } const $menu = $(`.${this.menuClass}`); - const $thumbsBtn = $menu.find('[data-name="thumbsup"], [data-name="thumbsdown"]').parent(); - const $userAuthored = this.isUserAuthored($addBtn); if ($menu.length) { if ($menu.is('.is-visible')) { $addBtn.removeClass('is-active'); @@ -134,9 +132,6 @@ export class AwardsHandler { }, 200); }); } - - $thumbsBtn.toggleClass('disabled', $userAuthored); - $thumbsBtn.prop('disabled', $userAuthored); } // Create the emoji menu with the first category of emojis. @@ -364,10 +359,6 @@ export class AwardsHandler { return $emojiButton.hasClass('active'); } - isUserAuthored($button) { - return $button.hasClass('js-user-authored'); - } - decrementCounter($emojiButton, emoji) { const counter = $('.js-counter', $emojiButton); const counterNumber = parseInt(counter.text(), 10); @@ -474,20 +465,16 @@ export class AwardsHandler { } postEmoji($emojiButton, awardUrl, emoji, callback) { - if (this.isUserAuthored($emojiButton)) { - this.userAuthored($emojiButton); - } else { - axios - .post(awardUrl, { - name: emoji, - }) - .then(({ data }) => { - if (data.ok) { - callback(); - } - }) - .catch(() => flash(__('Something went wrong on our end.'))); - } + axios + .post(awardUrl, { + name: emoji, + }) + .then(({ data }) => { + if (data.ok) { + callback(); + } + }) + .catch(() => flash(__('Something went wrong on our end.'))); } findEmojiIcon(votesBlock, emoji) { diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index 56a8d9430c7..0347f803757 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -146,13 +146,7 @@ export default { staged: false, prevPath: '', moved: false, - lastCommit: Object.assign(state.entries[file.path].lastCommit, { - id: lastCommit.commit.id, - url: lastCommit.commit_path, - message: lastCommit.commit.message, - author: lastCommit.commit.author_name, - updatedAt: lastCommit.commit.authored_date, - }), + lastCommitSha: lastCommit.commit.id, }); if (prevPath) { diff --git a/app/assets/javascripts/jobs/components/environments_block.vue b/app/assets/javascripts/jobs/components/environments_block.vue new file mode 100644 index 00000000000..ca6386595c7 --- /dev/null +++ b/app/assets/javascripts/jobs/components/environments_block.vue @@ -0,0 +1,118 @@ +<script> + import _ from 'underscore'; + import CiIcon from '~/vue_shared/components/ci_icon.vue'; + import { sprintf, __ } from '../../locale'; + + export default { + components: { + CiIcon, + }, + props: { + deploymentStatus: { + type: Object, + required: true, + }, + }, + computed: { + environment() { + let environmentText; + switch (this.deploymentStatus.status) { + case 'latest': + environmentText = sprintf( + __('This job is the most recent deployment to %{link}.'), + { link: this.environmentLink }, + false, + ); + break; + case 'out_of_date': + if (this.hasLastDeployment) { + environmentText = sprintf( + __( + 'This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}.', + ), + { + environmentLink: this.environmentLink, + deploymentLink: this.deploymentLink, + }, + false, + ); + } else { + environmentText = sprintf( + __('This job is an out-of-date deployment to %{environmentLink}.'), + { environmentLink: this.environmentLink }, + false, + ); + } + + break; + case 'failed': + environmentText = sprintf( + __('The deployment of this job to %{environmentLink} did not succeed.'), + { environmentLink: this.environmentLink }, + false, + ); + break; + case 'creating': + if (this.hasLastDeployment) { + environmentText = sprintf( + __( + 'This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}.', + ), + { + environmentLink: this.environmentLink, + deploymentLink: this.deploymentLink, + }, + false, + ); + } else { + environmentText = sprintf( + __('This job is creating a deployment to %{environmentLink}.'), + { environmentLink: this.environmentLink }, + false, + ); + } + break; + default: + break; + } + return environmentText; + }, + environmentLink() { + return sprintf( + '%{startLink}%{name}%{endLink}', + { + startLink: `<a href="${this.deploymentStatus.environment.path}">`, + name: _.escape(this.deploymentStatus.environment.name), + endLink: '</a>', + }, + false, + ); + }, + deploymentLink() { + return sprintf( + '%{startLink}%{name}%{endLink}', + { + startLink: `<a href="${this.lastDeployment.path}">`, + name: _.escape(this.lastDeployment.name), + endLink: '</a>', + }, + false, + ); + }, + hasLastDeployment() { + return this.deploymentStatus.environment.last_deployment; + }, + lastDeployment() { + return this.deploymentStatus.environment.last_deployment; + }, + }, + }; +</script> +<template> + <div class="prepend-top-default js-environment-container"> + <div class="environment-information"> + <ci-icon :status="deploymentStatus.icon" /> + <p v-html="environment"></p> + </div> + </div> +</template> diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue index 225d9f18612..e111d3b9ac2 100644 --- a/app/assets/javascripts/notes/components/note_awards_list.vue +++ b/app/assets/javascripts/notes/components/note_awards_list.vue @@ -82,29 +82,17 @@ export default { getAwardHTML(name) { return glEmojiTag(name); }, - getAwardClassBindings(awardList, awardName) { + getAwardClassBindings(awardList) { return { active: this.hasReactionByCurrentUser(awardList), - disabled: !this.canInteractWithEmoji(awardList, awardName), + disabled: !this.canInteractWithEmoji(), }; }, - canInteractWithEmoji(awardList, awardName) { - let isAllowed = true; - const restrictedEmojis = ['thumbsup', 'thumbsdown']; - - // Users can not add :+1: and :-1: to their own notes - if ( - this.getUserData.id === this.noteAuthorId && - restrictedEmojis.indexOf(awardName) > -1 - ) { - isAllowed = false; - } - - return this.getUserData.id && isAllowed; + canInteractWithEmoji() { + return this.getUserData.id; }, hasReactionByCurrentUser(awardList) { - return awardList.filter(award => award.user.id === this.getUserData.id) - .length; + return awardList.filter(award => award.user.id === this.getUserData.id).length; }, awardTitle(awardsList) { const hasReactionByCurrentUser = this.hasReactionByCurrentUser( @@ -197,7 +185,7 @@ export default { v-tooltip v-for="(awardList, awardName, index) in groupedAwards" :key="index" - :class="getAwardClassBindings(awardList, awardName)" + :class="getAwardClassBindings(awardList)" :title="awardTitle(awardList)" class="btn award-control" data-boundary="viewport" diff --git a/app/assets/javascripts/pages/groups/milestones/show/index.js b/app/assets/javascripts/pages/groups/milestones/show/index.js index 74cc4ba42c1..ebaea5ef3dc 100644 --- a/app/assets/javascripts/pages/groups/milestones/show/index.js +++ b/app/assets/javascripts/pages/groups/milestones/show/index.js @@ -1,8 +1,10 @@ import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; +import initDeleteMilestoneModal from '~/pages/milestones/shared/delete_milestone_modal_init'; + import Milestone from '~/milestone'; document.addEventListener('DOMContentLoaded', () => { initMilestonesShow(); - + initDeleteMilestoneModal(); Milestone.initDeprecationMessage(); }); diff --git a/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue index 4061c11ba8f..48668562f09 100644 --- a/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue +++ b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue @@ -40,8 +40,8 @@ if (this.issueCount === 0 && this.mergeRequestCount === 0) { return sprintf( s__(`Milestones| -You’re about to permanently delete the milestone %{milestoneTitle} from this project. -%{milestoneTitle} is not currently used in any issues or merge requests.`), +You’re about to permanently delete the milestone %{milestoneTitle}. +This milestone is not currently used in any issues or merge requests.`), { milestoneTitle, }, @@ -51,7 +51,7 @@ You’re about to permanently delete the milestone %{milestoneTitle} from this p return sprintf( s__(`Milestones| -You’re about to permanently delete the milestone %{milestoneTitle} from this project and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. +You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered.`), { milestoneTitle, diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js index 949219a0837..aea7b649c20 100644 --- a/app/assets/javascripts/pages/profiles/show/index.js +++ b/app/assets/javascripts/pages/profiles/show/index.js @@ -3,15 +3,22 @@ import createFlash from '~/flash'; import GfmAutoComplete from '~/gfm_auto_complete'; import EmojiMenu from './emoji_menu'; +const defaultStatusEmoji = 'speech_balloon'; + document.addEventListener('DOMContentLoaded', () => { const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu'; const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector); const statusEmojiField = document.getElementById('js-status-emoji-field'); const statusMessageField = document.getElementById('js-status-message-field'); - const findNoEmojiPlaceholder = () => document.getElementById('js-no-emoji-placeholder'); + const toggleNoEmojiPlaceholder = (isVisible) => { + const placeholderElement = document.getElementById('js-no-emoji-placeholder'); + placeholderElement.classList.toggle('hidden', !isVisible); + }; + + const findStatusEmoji = () => toggleEmojiMenuButton.querySelector('gl-emoji'); const removeStatusEmoji = () => { - const statusEmoji = toggleEmojiMenuButton.querySelector('gl-emoji'); + const statusEmoji = findStatusEmoji(); if (statusEmoji) { statusEmoji.remove(); } @@ -19,7 +26,7 @@ document.addEventListener('DOMContentLoaded', () => { const selectEmojiCallback = (emoji, emojiTag) => { statusEmojiField.value = emoji; - findNoEmojiPlaceholder().classList.add('hidden'); + toggleNoEmojiPlaceholder(false); removeStatusEmoji(); toggleEmojiMenuButton.innerHTML += emojiTag; }; @@ -29,7 +36,7 @@ document.addEventListener('DOMContentLoaded', () => { statusEmojiField.value = ''; statusMessageField.value = ''; removeStatusEmoji(); - findNoEmojiPlaceholder().classList.remove('hidden'); + toggleNoEmojiPlaceholder(true); }); const emojiAutocomplete = new GfmAutoComplete(); @@ -44,6 +51,23 @@ document.addEventListener('DOMContentLoaded', () => { selectEmojiCallback, ); emojiMenu.bindEvents(); + + const defaultEmojiTag = Emoji.glEmojiTag(defaultStatusEmoji); + statusMessageField.addEventListener('input', () => { + const hasStatusMessage = statusMessageField.value.trim() !== ''; + const statusEmoji = findStatusEmoji(); + if (hasStatusMessage && statusEmoji) { + return; + } + + if (hasStatusMessage) { + toggleNoEmojiPlaceholder(false); + toggleEmojiMenuButton.innerHTML += defaultEmojiTag; + } else if (statusEmoji.dataset.name === defaultStatusEmoji) { + toggleNoEmojiPlaceholder(true); + removeStatusEmoji(); + } + }); }) .catch(() => createFlash('Failed to load emoji list!')); }); diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js index 41880d27516..6e5ef0ac0b2 100644 --- a/app/assets/javascripts/performance_bar/index.js +++ b/app/assets/javascripts/performance_bar/index.js @@ -1,5 +1,4 @@ import Vue from 'vue'; -import Flash from '../flash'; import PerformanceBarService from './services/performance_bar_service'; import PerformanceBarStore from './stores/performance_bar_store'; @@ -46,7 +45,8 @@ export default ({ container }) => this.store.addRequestDetails(requestId, res.data.data); }) .catch(() => - Flash(`Error getting performance bar results for ${requestId}`), + // eslint-disable-next-line no-console + console.warn(`Error getting performance bar results for ${requestId}`), ); }, }, diff --git a/app/assets/javascripts/performance_bar/services/performance_bar_service.js b/app/assets/javascripts/performance_bar/services/performance_bar_service.js index bc71911ae35..60d9ba62570 100644 --- a/app/assets/javascripts/performance_bar/services/performance_bar_service.js +++ b/app/assets/javascripts/performance_bar/services/performance_bar_service.js @@ -11,13 +11,10 @@ export default class PerformanceBarService { static registerInterceptor(peekUrl, callback) { const interceptor = response => { - const requestId = response.headers['x-request-id']; - // Get the request URL from response.config for Axios, and response for - // Vue Resource. - const requestUrl = (response.config || response).url; - const cachedResponse = response.headers['x-gitlab-from-cache'] === 'true'; + const [fireCallback, requestId, requestUrl] = + PerformanceBarService.callbackParams(response, peekUrl); - if (requestUrl !== peekUrl && requestId && !cachedResponse) { + if (fireCallback) { callback(requestId, requestUrl); } @@ -38,4 +35,16 @@ export default class PerformanceBarService { vueResourceInterceptor, ); } + + static callbackParams(response, peekUrl) { + const requestId = response.headers && response.headers['x-request-id']; + // Get the request URL from response.config for Axios, and response for + // Vue Resource. + const requestUrl = (response.config || response).url; + const apiRequest = requestUrl && requestUrl.match(/^\/api\//); + const cachedResponse = response.headers && response.headers['x-gitlab-from-cache'] === 'true'; + const fireCallback = requestUrl !== peekUrl && requestId && !apiRequest && !cachedResponse; + + return [fireCallback, requestId, requestUrl]; + } } diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue index 8487c8036ee..2ad66f4fe86 100644 --- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue @@ -1,6 +1,5 @@ <script> import $ from 'jquery'; -import _ from 'underscore'; import JobNameComponent from './job_name_component.vue'; import JobComponent from './job_component.vue'; import tooltip from '../../../vue_shared/directives/tooltip'; @@ -47,7 +46,7 @@ export default { computed: { tooltipText() { - return _.escape(`${this.job.name} - ${this.job.status.label}`); + return `${this.job.name} - ${this.job.status.label}`; }, }, diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue index 66f95147193..9ac16b7e541 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue @@ -1,5 +1,4 @@ <script> -import _ from 'underscore'; import ActionComponent from './action_component.vue'; import JobNameComponent from './job_name_component.vue'; import tooltip from '../../../vue_shared/directives/tooltip'; @@ -62,7 +61,7 @@ export default { const textBuilder = []; if (this.job.name) { - textBuilder.push(_.escape(this.job.name)); + textBuilder.push(this.job.name); } if (this.job.name && this.status.tooltip) { @@ -106,7 +105,6 @@ export default { :class="cssClassJobName" :data-boundary="tooltipBoundary" data-container="body" - data-html="true" class="js-pipeline-graph-job-link" > @@ -122,7 +120,6 @@ export default { :title="tooltipText" :class="cssClassJobName" class="js-job-component-tooltip non-details-job-component" - data-html="true" data-container="body" > diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue index 21f21232596..d530ab2767b 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -1,5 +1,6 @@ <script> import Icon from '~/vue_shared/components/icon.vue'; +import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import timeagoMixin from '../../vue_shared/mixins/timeago'; import tooltip from '../../vue_shared/directives/tooltip'; import LoadingButton from '../../vue_shared/components/loading_button.vue'; @@ -16,6 +17,7 @@ export default { MemoryUsage, StatusIcon, Icon, + TooltipOnTruncate, }, directives: { tooltip, @@ -88,14 +90,20 @@ export default { <span> Deployed to </span> - <a - :href="deployment.url" - target="_blank" - rel="noopener noreferrer nofollow" - class="deploy-link js-deploy-meta" + <tooltip-on-truncate + :title="deployment.name" + truncate-target="child" + class="deploy-link label-truncate" > - {{ deployment.name }} - </a> + <a + :href="deployment.url" + target="_blank" + rel="noopener noreferrer nofollow" + class="js-deploy-meta" + > + {{ deployment.name }} + </a> + </tooltip-on-truncate> </template> <span v-tooltip diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue index a4c2289c590..72bd28ae03f 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue @@ -1,18 +1,17 @@ <script> -import tooltip from '~/vue_shared/directives/tooltip'; -import { n__ } from '~/locale'; +import _ from 'underscore'; +import { n__, s__, sprintf } from '~/locale'; import { mergeUrlParams, webIDEUrl } from '~/lib/utils/url_utility'; import Icon from '~/vue_shared/components/icon.vue'; import clipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; export default { name: 'MRWidgetHeader', - directives: { - tooltip, - }, components: { Icon, clipboardButton, + TooltipOnTruncate, }, props: { mr: { @@ -24,8 +23,12 @@ export default { shouldShowCommitsBehindText() { return this.mr.divergedCommitsCount > 0; }, - commitsText() { - return n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount); + commitsBehindText() { + return sprintf(s__('mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch'), { + commitsBehindLinkStart: `<a href="${_.escape(this.mr.targetBranchPath)}">`, + commitsBehind: n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount), + commitsBehindLinkEnd: '</a>', + }, false); }, branchNameClipboardData() { // This supports code in app/assets/javascripts/copy_to_clipboard.js that @@ -36,12 +39,6 @@ export default { gfm: `\`${this.mr.sourceBranch}\``, }); }, - isSourceBranchLong() { - return this.isBranchTitleLong(this.mr.sourceBranch); - }, - isTargetBranchLong() { - return this.isBranchTitleLong(this.mr.targetBranch); - }, webIdePath() { return mergeUrlParams({ target_project: this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath ? @@ -49,11 +46,6 @@ export default { }, webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`)); }, }, - methods: { - isBranchTitleLong(branchTitle) { - return branchTitle.length > 32; - }, - }, }; </script> <template> @@ -65,30 +57,21 @@ export default { <div class="normal"> <strong> {{ s__("mrWidget|Request to merge") }} - <span - :class="{ 'label-truncated': isSourceBranchLong }" - :title="isSourceBranchLong ? mr.sourceBranch : ''" - :v-tooltip="isSourceBranchLong" - class="label-branch js-source-branch" - data-placement="bottom" + <tooltip-on-truncate + :title="mr.sourceBranch" + truncate-target="child" + class="label-branch label-truncate js-source-branch" v-html="mr.sourceBranchLink" - > - </span> - - <clipboard-button + /><clipboard-button :text="branchNameClipboardData" :title="__('Copy branch name to clipboard')" css-class="btn-default btn-transparent btn-clipboard" /> - {{ s__("mrWidget|into") }} - - <span - :v-tooltip="isTargetBranchLong" - :class="{ 'label-truncatedtooltip': isTargetBranchLong }" - :title="isTargetBranchLong ? mr.targetBranch : ''" - class="label-branch" - data-placement="bottom" + <tooltip-on-truncate + :title="mr.targetBranch" + truncate-target="child" + class="label-branch label-truncate" > <a :href="mr.targetBranchTreePath" @@ -96,15 +79,13 @@ export default { > {{ mr.targetBranch }} </a> - </span> + </tooltip-on-truncate> </strong> <div v-if="shouldShowCommitsBehindText" class="diverged-commits-count" + v-html="commitsBehindText" > - <span class="monospace">{{ mr.sourceBranch }}</span> - is {{ commitsText }} - <span class="monospace">{{ mr.targetBranch }}</span> </div> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue index 4a3fd01fa39..fee41b239e8 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue @@ -3,6 +3,7 @@ import PipelineStage from '~/pipelines/components/stage.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import Icon from '~/vue_shared/components/icon.vue'; +import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; export default { name: 'MRWidgetPipeline', @@ -10,6 +11,7 @@ export default { PipelineStage, CiIcon, Icon, + TooltipOnTruncate, }, props: { pipeline: { @@ -30,6 +32,10 @@ export default { type: String, required: false, }, + sourceBranch: { + type: String, + required: false, + }, }, computed: { hasPipeline() { @@ -107,11 +113,12 @@ export default { > {{ pipeline.commit.short_id }}</a> on - <span - class="label-branch" + <tooltip-on-truncate + :title="sourceBranch" + truncate-target="child" + class="label-branch label-truncate" v-html="sourceBranchLink" - > - </span> + /> </template> </div> <div diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 80593d1f34a..dc6be025f11 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -254,6 +254,7 @@ export default { :pipeline="mr.pipeline" :ci-status="mr.ciStatus" :has-ci="mr.hasCI" + :source-branch="mr.sourceBranch" :source-branch-link="mr.sourceBranchLink" /> <deployment diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue index f44d361c47e..78fde463507 100644 --- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue +++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue @@ -71,7 +71,11 @@ export default { }, methods: { getPercent(count) { - return roundOffFloat((count / this.totalCount) * 100, 1); + const percent = roundOffFloat((count / this.totalCount) * 100, 1); + if (percent > 0 && percent < 1) { + return '< 1'; + } + return percent; }, barStyle(percent) { return `width: ${percent}%;`; diff --git a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue new file mode 100644 index 00000000000..125826da6c3 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue @@ -0,0 +1,67 @@ +<script> +import _ from 'underscore'; +import tooltip from '../directives/tooltip'; + +export default { + directives: { + tooltip, + }, + props: { + title: { + type: String, + required: false, + default: '', + }, + placement: { + type: String, + required: false, + default: 'top', + }, + truncateTarget: { + type: [String, Function], + required: false, + default: '', + }, + }, + data() { + return { + showTooltip: false, + }; + }, + mounted() { + const target = this.selectTarget(); + + if (target && target.scrollWidth > target.offsetWidth) { + this.showTooltip = true; + } + }, + methods: { + selectTarget() { + if (_.isFunction(this.truncateTarget)) { + return this.truncateTarget(this.$el); + } else if (this.truncateTarget === 'child') { + return this.$el.childNodes[0]; + } + + return this.$el; + }, + }, +}; +</script> + +<template> + <span + v-tooltip + v-if="showTooltip" + :title="title" + :data-placement="placement" + class="js-show-tooltip" + > + <slot></slot> + </span> + <span + v-else + > + <slot></slot> + </span> +</template> diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss index 1bd94c0acba..bdd7f09d926 100644 --- a/app/assets/stylesheets/framework/callout.scss +++ b/app/assets/stylesheets/framework/callout.scss @@ -25,25 +25,25 @@ /* Variations */ .bs-callout-danger { - background-color: $callout-danger-bg; - border-color: $callout-danger-border; - color: $callout-danger-color; + background-color: $red-100; + border-color: $red-200; + color: $red-700; } .bs-callout-warning { - background-color: $callout-warning-bg; - border-color: $callout-warning-border; - color: $callout-warning-color; + background-color: $orange-100; + border-color: $orange-200; + color: $orange-700; } .bs-callout-info { - background-color: $callout-info-bg; - border-color: $callout-info-border; - color: $callout-info-color; + background-color: $blue-100; + border-color: $blue-200; + color: $blue-700; } .bs-callout-success { - background-color: $callout-success-bg; - border-color: $callout-success-border; - color: $callout-success-color; + background-color: $green-100; + border-color: $green-200; + color: $green-700; } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 7c5380a90f7..72e27f9ad16 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -1,8 +1,8 @@ /** COLORS **/ -.cgray { color: $common-gray; } +.cgray { color: $gl-text-color; } .clgray { color: $common-gray-light; } -.cred { color: $common-red; } -.cgreen { color: $common-green; } +.cred { color: $red-500; } +.cgreen { color: $green-600; } .cdark { color: $common-gray-dark; } .text-plain, @@ -44,10 +44,10 @@ } .hint { font-style: italic; color: $hint-color; } -.light { color: $common-gray; } +.light { color: $gl-text-color; } .slead { - color: $common-gray; + color: $gl-text-color; font-size: 14px; margin-bottom: 12px; font-weight: $gl-font-weight-normal; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 83bc3776178..8a224dc517e 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -147,7 +147,7 @@ } @mixin dropdown-item-hover { - background-color: $dropdown-item-hover-bg; + background-color: $gray-darker; color: $gl-text-color; outline: 0; @@ -195,7 +195,7 @@ text-decoration: none; .badge.badge-pill { - background-color: darken($dropdown-link-hover-bg, 5%); + background-color: darken($blue-50, 5%); } } @@ -233,7 +233,7 @@ font-weight: $gl-font-weight-normal; padding: 8px 0; background-color: $white-light; - border: 1px solid $dropdown-border-color; + border: 1px solid $border-color; border-radius: $border-radius-base; box-shadow: 0 2px 4px $dropdown-shadow-color; @@ -874,7 +874,7 @@ header.header-content .dropdown-menu.frequent-items-dropdown-menu { overflow-y: auto; li.section-empty.section-failure { - color: $callout-danger-color; + color: $red-700; } .frequent-items-list-item-container a { diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss index a8ec1e1145a..6c50ea719d3 100644 --- a/app/assets/stylesheets/framework/emojis.scss +++ b/app/assets/stylesheets/framework/emojis.scss @@ -3,6 +3,6 @@ gl-emoji { display: inline-flex; vertical-align: middle; font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-size: 1.5em; - line-height: 0.9; + font-size: 1.4em; + line-height: 1em; } diff --git a/app/assets/stylesheets/framework/feature_highlight.scss b/app/assets/stylesheets/framework/feature_highlight.scss index cad915bc86f..85cabf43e9e 100644 --- a/app/assets/stylesheets/framework/feature_highlight.scss +++ b/app/assets/stylesheets/framework/feature_highlight.scss @@ -72,11 +72,11 @@ .feature-highlight-popover { width: 240px; padding: 0; - border: 1px solid $dropdown-border-color; + border: 1px solid $border-color; box-shadow: 0 2px 4px $dropdown-shadow-color; &.right > .arrow { - border-right-color: $dropdown-border-color; + border-right-color: $border-color; } .popover-body { diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 3bdf5bfc93a..1d3512bbb4c 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -312,11 +312,11 @@ span.idiff { text-decoration: none; .new-file { - color: $notify-new-file; + color: $green-600; } .deleted-file { - color: $notify-deleted-file; + color: $red-700; } } } diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index a70eece8f68..afd888af672 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -8,7 +8,7 @@ input { input[type='text'].danger { background: $input-danger-bg !important; - border-color: $input-danger-border; + border-color: $red-400; text-shadow: 0 1px 1px $white-light; } diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 88d2f0aaf85..3ae2c7078d6 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -58,7 +58,7 @@ .select2-drop.select2-drop-above { box-shadow: 0 2px 4px $dropdown-shadow-color; border-radius: $border-radius-base; - border: 1px solid $dropdown-border-color; + border: 1px solid $border-color; min-width: 175px; color: $gl-text-color; z-index: 999; @@ -69,7 +69,7 @@ } .select2-drop.select2-drop-above.select2-drop-active { - border-top: 1px solid $dropdown-border-color; + border-top: 1px solid $border-color; margin-top: -6px; } @@ -193,7 +193,7 @@ color: $gl-text-color; .select2-result-label { - background: $dropdown-item-hover-bg; + background: $gray-darker; } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 3531ce31794..2781d910b8d 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -319,7 +319,6 @@ $line-select-yellow-dark: #f0e2bd; $dark-diff-match-bg: rgba(255, 255, 255, 0.3); $dark-diff-match-color: rgba(255, 255, 255, 0.1); $file-mode-changed: #777; -$file-mode-changed: #777; $diff-image-info-color: gray; $diff-swipe-border: #999; $diff-view-modes-color: gray; @@ -342,9 +341,7 @@ $dropdown-min-height: 40px; $dropdown-max-height: 312px; $dropdown-vertical-offset: 4px; $dropdown-link-color: #555; -$dropdown-link-hover-bg: $blue-50; $dropdown-empty-row-bg: rgba(#000, 0.04); -$dropdown-border-color: $border-color; $dropdown-shadow-color: rgba(#000, 0.1); $dropdown-divider-color: rgba(#000, 0.1); $dropdown-title-btn-color: #bfbfbf; @@ -354,7 +351,6 @@ $dropdown-input-focus-shadow: rgba($blue-300, 0.4); $dropdown-loading-bg: rgba(#fff, 0.6); $dropdown-chevron-size: 10px; $dropdown-toggle-active-border-color: darken($border-color, 14%); -$dropdown-item-hover-bg: $gray-darker; $dropdown-fade-mask-height: 32px; $dropdown-member-form-control-width: 163px; @@ -496,22 +492,6 @@ $blame-blue: #254e77; $builds-trace-bg: #111; /* -* Callout -*/ -$callout-danger-bg: $red-100; -$callout-danger-border: $red-200; -$callout-danger-color: $red-700; -$callout-warning-bg: $orange-100; -$callout-warning-border: $orange-200; -$callout-warning-color: $orange-700; -$callout-info-bg: $blue-100; -$callout-info-border: $blue-200; -$callout-info-color: $blue-700; -$callout-success-bg: $green-100; -$callout-success-border: $green-200; -$callout-success-color: $green-700; - -/* * Commit Page */ $commit-max-width-marker-color: rgba(0, 0, 0, 0); @@ -520,16 +500,8 @@ $commit-message-text-area-bg: rgba(0, 0, 0, 0); /* * Common */ -$common-gray: $gl-text-color; $common-gray-light: #bbb; $common-gray-dark: #444; -$common-red: $red-500; -$common-green: $green-600; - -/* -* Editor -*/ -$editor-cancel-color: $red-600; /* * Events @@ -550,7 +522,6 @@ $logs-p-color: #333; */ $input-height: 34px; $input-danger-bg: #f2dede; -$input-danger-border: $red-400; $input-group-addon-bg: #f7f8fa; $gl-field-focus-shadow: rgba(0, 0, 0, 0.075); $gl-field-focus-shadow-error: rgba($red-500, 0.6); @@ -597,16 +568,9 @@ $fade-mask-transition-duration: 0.1s; $fade-mask-transition-curve: ease-in-out; /* -* Lint -*/ -$lint-incorrect-color: $red-500; -$lint-correct-color: $green-500; - -/* * Login */ $login-brand-holder-color: #888; -$login-devise-error-color: $red-700; /* * Nav @@ -619,15 +583,12 @@ $nav-toggle-gray: #666; */ $notify-details: #777; $notify-footer: #777; -$notify-new-file: $green-600; -$notify-deleted-file: $red-700; /* * Projects */ $project-option-descr-color: #54565b; $project-breadcrumb-color: #999; -$project-private-forks-notice-odd: $green-600; $project-network-controls-color: #888; $feature-toggle-color: #fff; @@ -636,21 +597,10 @@ $feature-toggle-color-disabled: #999; $feature-toggle-color-enabled: #4a8bee; /* -* Runners -*/ -$runner-state-shared-bg: $green-400; -$runner-state-specific-bg: $blue-400; -$runner-status-online-color: $green-600; -$runner-status-offline-color: $gray-darkest; -$runner-status-paused-color: $red-500; - -/* Stat Graph */ $stat-graph-common-bg: #f3f3f3; -$stat-graph-area-fill: $green-500; $stat-graph-axis-fill: #aaa; -$stat-graph-orange-fill: $orange-500; $stat-graph-selection-fill: #333; $stat-graph-selection-stroke: #333; @@ -663,7 +613,6 @@ $select2-drop-shadow2: rgba(31, 37, 50, 0.317647); /* * Todo */ -$todo-alert-blue: $blue-500; $todo-body-pre-color: #777; $todo-body-border: #ddd; @@ -686,7 +635,6 @@ $ui-dev-kit-example-border: #ddd; /* Pipeline Graph */ -$stage-hover-bg: $gray-darker; $ci-action-icon-size: 22px; $ci-action-icon-size-lg: 24px; $pipeline-dropdown-line-height: 20px; @@ -714,13 +662,6 @@ Animation Functions $dropdown-animation-timing: cubic-bezier(0.23, 1, 0.32, 1); /* -Convdev Index -*/ -$color-high-score: $green-400; -$color-average-score: $orange-400; -$color-low-score: $red-400; - -/* Performance Bar */ $perf-bar-text: #999; @@ -761,9 +702,5 @@ Modals */ $modal-body-height: 134px; -/* -Prometheus -*/ -$prometheus-table-row-highlight-color: $theme-gray-100; $priority-label-empty-state-width: 114px; diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 1696d18584d..14ba8b1df83 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -397,7 +397,7 @@ } &:hover { - background-color: $dropdown-item-hover-bg; + background-color: $gray-darker; } .icon-retry { diff --git a/app/assets/stylesheets/pages/convdev_index.scss b/app/assets/stylesheets/pages/convdev_index.scss index bd338326154..52fcdf4a405 100644 --- a/app/assets/stylesheets/pages/convdev_index.scss +++ b/app/assets/stylesheets/pages/convdev_index.scss @@ -80,7 +80,7 @@ $space-between-cards: 8px; } .convdev-card-low { - border-top-color: $color-low-score; + border-top-color: $red-400; .board-card-score-big { background-color: $red-50; @@ -88,7 +88,7 @@ $space-between-cards: 8px; } .convdev-card-average { - border-top-color: $color-average-score; + border-top-color: $orange-400; .board-card-score-big { background-color: $orange-50; @@ -96,7 +96,7 @@ $space-between-cards: 8px; } .convdev-card-high { - border-top-color: $color-high-score; + border-top-color: $green-400; .board-card-score-big { background-color: $green-50; @@ -243,13 +243,13 @@ $space-between-cards: 8px; } .convdev-high-score { - color: $color-high-score; + color: $green-400; } .convdev-average-score { - color: $color-average-score; + color: $orange-400; } .convdev-low-score { - color: $color-low-score; + color: $red-400; } diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 892da152b5f..04570c057d1 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -23,10 +23,10 @@ } .cancel-btn { - color: $editor-cancel-color; + color: $red-600; &:hover { - color: $editor-cancel-color; + color: $red-600; } } diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 179c0964567..196f6ae6d8c 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -501,5 +501,5 @@ } .prometheus-table-row-highlight { - background-color: $prometheus-table-row-highlight-color; + background-color: $theme-gray-100; } diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 8a4a2caa6c9..c9e5fb9c579 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -186,7 +186,7 @@ h2 { margin-top: 0; font-size: 14px; - color: $login-devise-error-color; + color: $red-700; } } } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 5631d943984..7b8cad254c7 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -195,6 +195,7 @@ .ci-widget-content { display: flex; align-items: center; + flex: 1; } } @@ -222,6 +223,7 @@ .normal { flex: 1; + flex-basis: auto; } .capitalize { @@ -235,22 +237,23 @@ font-weight: normal; overflow: hidden; word-break: break-all; + } - &.label-truncated { - position: relative; - display: inline-block; - width: 250px; - margin-bottom: -3px; - white-space: nowrap; - text-overflow: clip; - line-height: 14px; - - &::after { - position: absolute; - content: '...'; - right: 0; - font-family: $regular-font; - background-color: $gray-light; + .deploy-link, + .label-branch { + &.label-truncate { + // NOTE: This selector targets its children because some of the HTML comes from + // 'source_branch_link'. Once this external HTML is no longer used, we could + // simplify this. + > a, + > span { + display: inline-block; + max-width: 12.5em; + margin-bottom: -3px; + white-space: nowrap; + text-overflow: ellipsis; + line-height: 14px; + overflow: hidden; } } } @@ -582,7 +585,7 @@ @include media-breakpoint-down(md) { flex-direction: column; - align-items: flex-start; + align-items: stretch; .branch-actions { margin-top: 16px; @@ -593,13 +596,13 @@ .branch-actions { align-self: center; margin-left: $gl-padding; + white-space: nowrap; } } } .diverged-commits-count { color: $gl-text-color-secondary; - font-size: 12px; } } @@ -918,7 +921,7 @@ flex: 1; flex-direction: row; - @include media-breakpoint-down(md) { + @include media-breakpoint-down(sm) { flex-direction: column; .stage-cell .stage-container { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index fce04c58c24..dbe9f0c03fb 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -141,6 +141,9 @@ ul.notes { } .note-body { + overflow-x: auto; + overflow-y: hidden; + .note-text { @include md-typography; // Reset ul style types since we're nested inside a ul already diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index ad057ed3c83..8bb8b83dc5e 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -19,7 +19,7 @@ background-color: $white-light; &:hover { - background-color: $stage-hover-bg; + background-color: $gray-darker; border: 1px solid $dropdown-toggle-active-border-color; color: $gl-text-color; } @@ -595,7 +595,7 @@ a.build-content:hover, button.build-content:hover { - background-color: $stage-hover-bg; + background-color: $gray-darker; border: 1px solid $dropdown-toggle-active-border-color; } @@ -668,7 +668,7 @@ display: block; &:hover { - background-color: $stage-hover-bg; + background-color: $gray-darker; border: 1px solid $dropdown-toggle-active-border-color; svg { @@ -835,7 +835,7 @@ button.mini-pipeline-graph-dropdown-toggle { display: block; &:hover { - background-color: $stage-hover-bg; + background-color: $gray-darker; border: 1px solid $dropdown-toggle-active-border-color; svg { @@ -934,7 +934,7 @@ button.mini-pipeline-graph-dropdown-toggle { &:focus { outline: none; text-decoration: none; - background-color: $stage-hover-bg; + background-color: $gray-darker; } } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 3154bcf5d0f..a95e78931b1 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -900,7 +900,7 @@ pre.light-well { .private-forks-notice .private-fork-icon { i:nth-child(1) { - color: $project-private-forks-notice-odd; + color: $green-600; } i:nth-child(2) { @@ -1128,12 +1128,12 @@ pre.light-well { .project-ci-body { .incorrect-syntax { font-size: 18px; - color: $lint-incorrect-color; + color: $red-500; } .correct-syntax { font-size: 18px; - color: $lint-correct-color; + color: $green-500; } } diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss index 2734faec558..59f01f3e958 100644 --- a/app/assets/stylesheets/pages/runners.scss +++ b/app/assets/stylesheets/pages/runners.scss @@ -4,24 +4,24 @@ color: $white-light; &.runner-state-shared { - background: $runner-state-shared-bg; + background: $green-400; } &.runner-state-specific { - background: $runner-state-specific-bg; + background: $blue-400; } } .runner-status-online { - color: $runner-status-online-color; + color: $green-600; } .runner-status-offline { - color: $runner-status-offline-color; + color: $gray-darkest; } .runner-status-paused { - color: $runner-status-paused-color; + color: $red-500; } .runner { diff --git a/app/assets/stylesheets/pages/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss index 3f6f5f06075..d331edaa302 100644 --- a/app/assets/stylesheets/pages/stat_graph.scss +++ b/app/assets/stylesheets/pages/stat_graph.scss @@ -5,7 +5,7 @@ } .area { - fill: $stat-graph-area-fill; + fill: $green-500; fill-opacity: 0.5; } @@ -54,7 +54,7 @@ } .area-contributor { - fill: $stat-graph-orange-fill; + fill: $orange-500; } } } diff --git a/app/controllers/concerns/renders_commits.rb b/app/controllers/concerns/renders_commits.rb index fb41dc1e8a8..b1c9b1e532f 100644 --- a/app/controllers/concerns/renders_commits.rb +++ b/app/controllers/concerns/renders_commits.rb @@ -1,4 +1,24 @@ module RendersCommits + def limited_commits(commits) + if commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE + [ + commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE), + commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE + ] + else + [commits, 0] + end + end + + # This is used as a helper method in a controller. + # rubocop: disable Gitlab/ModuleWithInstanceVariables + def set_commits_for_rendering(commits) + @total_commit_count = commits.size + limited, @hidden_commit_count = limited_commits(commits) + prepare_commits_for_rendering(limited) + end + # rubocop: enable Gitlab/ModuleWithInstanceVariables + def prepare_commits_for_rendering(commits) Banzai::CommitRenderer.render(commits, @project, current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables diff --git a/app/controllers/concerns/toggle_award_emoji.rb b/app/controllers/concerns/toggle_award_emoji.rb index ba5b7d33f87..ae0b815f85e 100644 --- a/app/controllers/concerns/toggle_award_emoji.rb +++ b/app/controllers/concerns/toggle_award_emoji.rb @@ -5,7 +5,7 @@ module ToggleAwardEmoji authenticate_user! name = params.require(:name) - if awardable.user_can_award?(current_user, name) + if awardable.user_can_award?(current_user) awardable.toggle_award_emoji(name, current_user) todoable = to_todoable(awardable) diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 9bd51de7e97..6bdc0f79ef2 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -2,8 +2,8 @@ class Groups::MilestonesController < Groups::ApplicationController include MilestoneActions before_action :group_projects - before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels] - before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update] + before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy] + before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy] def index respond_to do |format| @@ -56,10 +56,21 @@ class Groups::MilestonesController < Groups::ApplicationController redirect_to milestone_path end + def destroy + return render_404 if @milestone.legacy_group_milestone? + + Milestones::DestroyService.new(group, current_user).execute(@milestone) + + respond_to do |format| + format.html { redirect_to group_milestones_path(group), status: :see_other } + format.js { head :ok } + end + end + private def authorize_admin_milestones! - return render_404 unless can?(current_user, :admin_milestones, group) + return render_404 unless can?(current_user, :admin_milestone, group) end def milestone_params diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 36faea8056e..5546bef850b 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -63,7 +63,7 @@ class Projects::CommitsController < Projects::ApplicationController end @commits = @commits.with_pipeline_status - @commits = prepare_commits_for_rendering(@commits) + @commits = set_commits_for_rendering(@commits) end # Rails 5 sets request.format from the extension. diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index f93e500a07a..a1e12821caf 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -78,7 +78,7 @@ class Projects::CompareController < Projects::ApplicationController end def define_commits - @commits = compare.present? ? prepare_commits_for_rendering(compare.commits) : [] + @commits = compare.present? ? set_commits_for_rendering(@compare.commits) : [] end def define_diffs diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index ef8159aa553..c3ac8e107fb 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -113,7 +113,7 @@ class Projects::IssuesController < Projects::ApplicationController end def referenced_merge_requests - @merge_requests, @closed_by_merge_requests = ::Issues::FetchReferencedMergeRequestsService.new(project, current_user).execute(issue) + @merge_requests, @closed_by_merge_requests = ::Issues::ReferencedMergeRequestsService.new(project, current_user).execute(issue) respond_to do |format| format.json do diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index 81129456ad8..03d0290ac1d 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -101,7 +101,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap @target_project = @merge_request.target_project @source_project = @merge_request.source_project - @commits = prepare_commits_for_rendering(@merge_request.commits) + @commits = set_commits_for_rendering(@merge_request.commits) @commit = @merge_request.diff_head_commit @labels = LabelsFinder.new(current_user, project_id: @project.id).execute diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 1b069fe507b..d31b58972ca 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -79,7 +79,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo # Get commits from repository # or from cache if already merged @commits = - prepare_commits_for_rendering(@merge_request.commits.with_pipeline_status) + set_commits_for_rendering(@merge_request.commits.with_pipeline_status) render json: { html: view_to_html_string('projects/merge_requests/_commits') } end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 89fe90fd801..7a942c44ac4 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -210,17 +210,6 @@ module CommitsHelper Sanitize.clean(string, remove_contents: true) end - def limited_commits(commits) - if commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE - [ - commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE), - commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE - ] - else - [commits, 0] - end - end - def commit_path(project, commit, merge_request: nil) if merge_request&.persisted? diffs_project_merge_request_path(project, merge_request, commit_id: commit.id) diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb index 4664b1728c4..c65f1565425 100644 --- a/app/helpers/import_helper.rb +++ b/app/helpers/import_helper.rb @@ -5,6 +5,10 @@ module ImportHelper false end + def sanitize_project_name(name) + name.gsub(/[^\w\-]/, '-') + end + def import_project_target(owner, name) namespace = current_user.can_create_group? ? owner : current_user.namespace_path "#{namespace}/#{name}" diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 96dc7ae1185..5b27d1d9404 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -92,14 +92,6 @@ module IssuesHelper end end - def award_user_authored_class(award) - if award == 'thumbsdown' || award == 'thumbsup' - 'user-authored js-user-authored' - else - '' - end - end - def awards_sort(awards) awards.sort_by do |award, award_emojis| if award == "thumbsup" diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 6b4079b4113..18b3badda8d 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -447,7 +447,7 @@ module ProjectsHelper end def project_permissions_panel_data(project) - data = { + { currentSettings: project_permissions_settings(project), canChangeVisibilityLevel: can_change_visibility_level?(project, current_user), allowedVisibilityOptions: project_allowed_visibility_levels(project), @@ -457,8 +457,10 @@ module ProjectsHelper lfsAvailable: Gitlab.config.lfs.enabled, lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') } + end - data.to_json.html_safe + def project_permissions_panel_data_json(project) + project_permissions_panel_data(project).to_json.html_safe end def project_allowed_visibility_levels(project) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index e4aed76f611..526bf7af99b 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -649,8 +649,7 @@ module Ci def keep_around_commits return unless project - project.repository.keep_around(self.sha) - project.repository.keep_around(self.before_sha) + project.repository.keep_around(self.sha, self.before_sha) end def valid_source diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index 49981db0d80..4200253053a 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -76,12 +76,8 @@ module Awardable true end - def awardable_votes?(name) - AwardEmoji::UPVOTE_NAME == name || AwardEmoji::DOWNVOTE_NAME == name - end - - def user_can_award?(current_user, name) - awardable_by_user?(current_user, name) && Ability.allowed?(current_user, :award_emoji, self) + def user_can_award?(current_user) + Ability.allowed?(current_user, :award_emoji, self) end def user_authored?(current_user) @@ -117,12 +113,4 @@ module Awardable def normalize_name(name) Gitlab::Emoji.normalize_emoji_name(name) end - - def awardable_by_user?(current_user, name) - if user_authored?(current_user) - !awardable_votes?(normalize_name(name)) - else - true - end - end end diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 58d949315e0..716cf6574d3 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -191,14 +191,18 @@ class DiffNote < Note end def keep_around_commits - project.repository.keep_around(self.original_position.base_sha) - project.repository.keep_around(self.original_position.start_sha) - project.repository.keep_around(self.original_position.head_sha) + shas = [ + self.original_position.base_sha, + self.original_position.start_sha, + self.original_position.head_sha + ] if self.position != self.original_position - project.repository.keep_around(self.position.base_sha) - project.repository.keep_around(self.position.start_sha) - project.repository.keep_around(self.position.head_sha) + shas << self.position.base_sha + shas << self.position.start_sha + shas << self.position.head_sha end + + project.repository.keep_around(*shas) end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 94cf12f3c2b..d0cd7461daa 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -170,27 +170,6 @@ class Issue < ActiveRecord::Base "#{project.to_reference(from, full: full)}#{reference}" end - def referenced_merge_requests(current_user = nil) - ext = all_references(current_user) - - notes_with_associations.each do |object| - object.all_references(current_user, extractor: ext) - end - - merge_requests = ext.merge_requests.sort_by(&:iid) - - cross_project_filter = -> (merge_requests) do - merge_requests.select { |mr| mr.target_project == project } - end - - Ability.merge_requests_readable_by_user( - merge_requests, current_user, - filters: { - read_cross_project: cross_project_filter - } - ) - end - # All branches containing the current issue's ID, except for # those with a merge request open referencing the current issue. def related_branches(current_user) @@ -198,7 +177,11 @@ class Issue < ActiveRecord::Base branch =~ /\A#{iid}-(?!\d+-stable)/i end - branches_with_merge_request = self.referenced_merge_requests(current_user).map(&:source_branch) + branches_with_merge_request = + Issues::ReferencedMergeRequestsService + .new(project, current_user) + .referenced_merge_requests(self) + .map(&:source_branch) branches_with_iid - branches_with_merge_request end @@ -225,26 +208,6 @@ class Issue < ActiveRecord::Base project end - # From all notes on this issue, we'll select the system notes about linked - # merge requests. Of those, the MRs closing `self` are returned. - def closed_by_merge_requests(current_user = nil) - return [] unless open? - - ext = all_references(current_user) - - notes.system.each do |note| - note.all_references(current_user, extractor: ext) - end - - merge_requests = ext.merge_requests.select(&:open?) - if merge_requests.any? - ids = MergeRequestsClosingIssues.where(merge_request_id: merge_requests.map(&:id), issue_id: id).pluck(:merge_request_id) - merge_requests.select { |mr| mr.id.in?(ids) } - else - [] - end - end - def moved? !moved_to.nil? end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index d9393b4e545..bbe4f6f7969 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -314,9 +314,7 @@ class MergeRequestDiff < ActiveRecord::Base def keep_around_commits [repository, merge_request.source_project.repository].uniq.each do |repo| - repo.keep_around(start_commit_sha) - repo.keep_around(head_commit_sha) - repo.keep_around(base_commit_sha) + repo.keep_around(start_commit_sha, head_commit_sha, base_commit_sha) end end end diff --git a/app/models/project.rb b/app/models/project.rb index 8f631d7f0ed..67593c9b2fe 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -85,8 +85,7 @@ class Project < ActiveRecord::Base after_create :create_project_feature, unless: :project_feature after_create -> { SiteStatistic.track(STATISTICS_ATTRIBUTE) } - before_destroy ->(project) { project.project_feature.untrack_statistics_for_deletion! } - after_destroy -> { SiteStatistic.untrack(STATISTICS_ATTRIBUTE) } + before_destroy :untrack_site_statistics after_create :create_ci_cd_settings, unless: :ci_cd_settings, @@ -2072,13 +2071,19 @@ class Project < ActiveRecord::Base private def rename_or_migrate_repository! - if Gitlab::CurrentSettings.hashed_storage_enabled? && storage_version != LATEST_STORAGE_VERSION + if Gitlab::CurrentSettings.hashed_storage_enabled? && + storage_upgradable? && + Feature.disabled?(:skip_hashed_storage_upgrade) # kill switch in case we need to disable upgrade behavior ::Projects::HashedStorageMigrationService.new(self, full_path_was).execute else storage.rename_repo end end + def storage_upgradable? + storage_version != LATEST_STORAGE_VERSION + end + def after_rename_repository(full_path_before, path_before) execute_rename_repository_hooks!(full_path_before) @@ -2093,6 +2098,11 @@ class Project < ActiveRecord::Base Gitlab::PagesTransfer.new.rename_project(path_before, self.path, namespace.full_path) end + def untrack_site_statistics + SiteStatistic.untrack(STATISTICS_ATTRIBUTE) + self.project_feature.untrack_statistics_for_deletion! + end + def execute_rename_repository_hooks!(full_path_before) # When we import a project overwriting the original project, there # is a move operation. In that case we don't want to send the instructions. diff --git a/app/models/protected_tag.rb b/app/models/protected_tag.rb index a36f0d36262..94746141945 100644 --- a/app/models/protected_tag.rb +++ b/app/models/protected_tag.rb @@ -4,6 +4,8 @@ class ProtectedTag < ActiveRecord::Base include Gitlab::ShellAdapter include ProtectedRef + validates :name, uniqueness: { scope: :project_id } + protected_ref_access_levels :create def self.protected?(project, ref_name) diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index 833faf3bc82..c1f53b5da4f 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -150,6 +150,15 @@ class RemoteMirror < ActiveRecord::Base result.to_s end + def ensure_remote! + return unless project + return unless remote_name && url + + # If this fails or the remote already exists, we won't know due to + # https://gitlab.com/gitlab-org/gitaly/issues/1317 + project.repository.add_remote(remote_name, url) + end + private def raw diff --git a/app/models/repository.rb b/app/models/repository.rb index 69f375dc6f3..cf255c8951f 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -247,15 +247,22 @@ class Repository # Git GC will delete commits from the repository that are no longer in any # branches or tags, but we want to keep some of these commits around, for # example if they have comments or CI builds. - def keep_around(sha) - return unless sha.present? && commit_by(oid: sha) + # + # For Geo's sake, pass in multiple shas rather than calling it multiple times, + # to avoid unnecessary syncing. + def keep_around(*shas) + shas.each do |sha| + begin + next unless sha.present? && commit_by(oid: sha) - return if kept_around?(sha) + next if kept_around?(sha) - # This will still fail if the file is corrupted (e.g. 0 bytes) - raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false) - rescue Gitlab::Git::CommandError => ex - Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}" + # This will still fail if the file is corrupted (e.g. 0 bytes) + raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false) + rescue Gitlab::Git::CommandError => ex + Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}" + end + end end def kept_around?(sha) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index a8d7a05f509..73c93b22c95 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -53,7 +53,7 @@ class GroupPolicy < BasePolicy rule { has_access }.enable :read_namespace - rule { developer }.enable :admin_milestones + rule { developer }.enable :admin_milestone rule { reporter }.policy do enable :admin_label @@ -72,6 +72,8 @@ class GroupPolicy < BasePolicy enable :admin_namespace enable :admin_group_member enable :change_visibility_level + + enable :set_note_created_at end rule { can?(:read_nested_project_resources) }.policy do diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 00c58f15013..fd6cc504a3b 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -143,6 +143,10 @@ class ProjectPolicy < BasePolicy enable :destroy_merge_request enable :destroy_issue enable :remove_pages + + enable :set_issue_iid + enable :set_issue_created_at + enable :set_note_created_at end rule { can?(:guest_access) }.policy do diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 156b4b3c6ec..26e90e8cf8c 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -158,7 +158,7 @@ class GitPushService < BaseService end def process_default_branch - offset = [push_commits_count - PROCESS_COMMIT_LIMIT, 0].max + offset = [push_commits_count_for_ref - PROCESS_COMMIT_LIMIT, 0].max @push_commits = project.repository.commits(params[:newrev], offset: offset, limit: PROCESS_COMMIT_LIMIT) project.after_create_default_branch @@ -172,7 +172,7 @@ class GitPushService < BaseService params[:newrev], params[:ref], @push_commits, - commits_count: push_commits_count) + commits_count: commits_count) end def push_to_existing_branch? @@ -213,8 +213,14 @@ class GitPushService < BaseService end end - def push_commits_count - strong_memoize(:push_commits_count) do + def commits_count + return push_commits_count_for_ref if default_branch? && push_to_new_branch? + + Array(@push_commits).size + end + + def push_commits_count_for_ref + strong_memoize(:push_commits_count_for_ref) do project.repository.commit_count_for_ref(params[:ref]) end end diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb index 12aeba4af71..93d84bd8a9c 100644 --- a/app/services/groups/destroy_service.rb +++ b/app/services/groups/destroy_service.rb @@ -12,12 +12,15 @@ module Groups def execute group.prepare_for_destroy - group.projects.each do |project| + group.projects.includes(:project_feature).each do |project| # Execute the destruction of the models immediately to ensure atomic cleanup. success = ::Projects::DestroyService.new(project, current_user).execute raise DestroyError, "Project #{project.id} can't be deleted" unless success end + # reload the relation to prevent triggering destroy hooks on the projects again + group.projects.reload + group.children.each do |group| # This needs to be synchronous since the namespace gets destroyed below DestroyService.new(group, current_user).execute diff --git a/app/services/issues/fetch_referenced_merge_requests_service.rb b/app/services/issues/fetch_referenced_merge_requests_service.rb deleted file mode 100644 index 5e84f3c81c9..00000000000 --- a/app/services/issues/fetch_referenced_merge_requests_service.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -module Issues - class FetchReferencedMergeRequestsService < Issues::BaseService - def execute(issue) - referenced_merge_requests = issue.referenced_merge_requests(current_user) - referenced_merge_requests = Gitlab::IssuableSorter.sort(project, referenced_merge_requests) { |i| i.iid.to_s } - closed_by_merge_requests = issue.closed_by_merge_requests(current_user) - closed_by_merge_requests = Gitlab::IssuableSorter.sort(project, closed_by_merge_requests) { |i| i.iid.to_s } - - [referenced_merge_requests, closed_by_merge_requests] - end - end -end diff --git a/app/services/issues/referenced_merge_requests_service.rb b/app/services/issues/referenced_merge_requests_service.rb new file mode 100644 index 00000000000..40d78502697 --- /dev/null +++ b/app/services/issues/referenced_merge_requests_service.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Issues + class ReferencedMergeRequestsService < Issues::BaseService + def execute(issue) + referenced = referenced_merge_requests(issue) + closed_by = closed_by_merge_requests(issue) + preloader = ActiveRecord::Associations::Preloader.new + + preloader.preload(referenced + closed_by, + head_pipeline: { project: [:route, { namespace: :route }] }) + + [sort_by_iid(referenced), sort_by_iid(closed_by)] + end + + def referenced_merge_requests(issue) + merge_requests = extract_merge_requests(issue) + + cross_project_filter = -> (merge_requests) do + merge_requests.select { |mr| mr.target_project == project } + end + + Ability.merge_requests_readable_by_user( + merge_requests, + current_user, + filters: { + read_cross_project: cross_project_filter + } + ) + end + + def closed_by_merge_requests(issue) + return [] unless issue.open? + + merge_requests = extract_merge_requests(issue, filter: :system).select(&:open?) + + return [] if merge_requests.empty? + + ids = MergeRequestsClosingIssues.where(merge_request_id: merge_requests.map(&:id), issue_id: issue.id).pluck(:merge_request_id) + merge_requests.select { |mr| mr.id.in?(ids) } + end + + private + + def extract_merge_requests(issue, filter: nil) + ext = issue.all_references(current_user) + notes = issue_notes(issue) + notes = notes.select(&filter) if filter + + notes.each do |note| + note.all_references(current_user, extractor: ext) + end + + ext.merge_requests + end + + def issue_notes(issue) + @issue_notes ||= {} + @issue_notes[issue] ||= issue.notes.includes(:author) + end + + def sort_by_iid(merge_requests) + Gitlab::IssuableSorter.sort(project, merge_requests) { |mr| mr.iid.to_s } + end + end +end diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index bc988eb2a26..55750269bb4 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -128,8 +128,7 @@ module MergeRequests # def assign_title_and_description assign_title_and_description_from_single_commit - assign_title_from_issue if target_project.issues_enabled? || target_project.external_issue_tracker - + merge_request.title ||= title_from_issue if target_project.issues_enabled? || target_project.external_issue_tracker merge_request.title ||= source_branch.titleize.humanize merge_request.title = wip_title if compare_commits.empty? @@ -159,20 +158,18 @@ module MergeRequests merge_request.description ||= commit.description.try(:strip) end - def assign_title_from_issue + def title_from_issue return unless issue - merge_request.title = "Resolve \"#{issue.title}\"" if issue.is_a?(Issue) + return "Resolve \"#{issue.title}\"" if issue.is_a?(Issue) - return if merge_request.title.present? + return if issue_iid.blank? - if issue_iid.present? - title_parts = ["Resolve #{issue.to_reference}"] - branch_title = source_branch.downcase.remove(issue_iid.downcase).titleize.humanize + title_parts = ["Resolve #{issue.to_reference}"] + branch_title = source_branch.downcase.remove(issue_iid.downcase).titleize.humanize - title_parts << "\"#{branch_title}\"" if branch_title.present? - merge_request.title = title_parts.join(' ') - end + title_parts << "\"#{branch_title}\"" if branch_title.present? + title_parts.join(' ') end def issue_iid diff --git a/app/services/milestones/destroy_service.rb b/app/services/milestones/destroy_service.rb index 15c04525075..7cda802c120 100644 --- a/app/services/milestones/destroy_service.rb +++ b/app/services/milestones/destroy_service.rb @@ -3,8 +3,6 @@ module Milestones class DestroyService < Milestones::BaseService def execute(milestone) - return unless milestone.project_milestone? - Milestone.transaction do update_params = { milestone: nil } @@ -16,15 +14,21 @@ module Milestones MergeRequests::UpdateService.new(parent, current_user, update_params).execute(merge_request) end - event_service.destroy_milestone(milestone, current_user) - - Event.for_milestone_id(milestone.id).each do |event| - event.target_id = nil - event.save - end + log_destroy_event_for(milestone) milestone.destroy end end + + def log_destroy_event_for(milestone) + return if milestone.group_milestone? + + event_service.destroy_milestone(milestone, current_user) + + Event.for_milestone_id(milestone.id).each do |event| + event.target_id = nil + event.save + end + end end end diff --git a/app/services/projects/update_remote_mirror_service.rb b/app/services/projects/update_remote_mirror_service.rb index 4651f7c4f8f..591b38b8151 100644 --- a/app/services/projects/update_remote_mirror_service.rb +++ b/app/services/projects/update_remote_mirror_service.rb @@ -10,6 +10,7 @@ module Projects return success unless remote_mirror.enabled? begin + remote_mirror.ensure_remote! repository.fetch_remote(remote_mirror.remote_name, no_tags: true) opts = {} diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 8838ed06324..a4c4c9e4812 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -402,7 +402,7 @@ module QuickActions match[1] if match end command :award do |name| - if name && issuable.user_can_award?(current_user, name) + if name && issuable.user_can_award?(current_user) @updates[:emoji_award] = name end end diff --git a/app/views/admin/hook_logs/show.html.haml b/app/views/admin/hook_logs/show.html.haml index 2eb3ac85722..86729dbe7bc 100644 --- a/app/views/admin/hook_logs/show.html.haml +++ b/app/views/admin/hook_logs/show.html.haml @@ -4,7 +4,6 @@ %hr -= link_to 'Resend Request', retry_admin_hook_hook_log_path(@hook, @hook_log), class: "btn btn-default float-right prepend-left-10" += link_to 'Resend Request', retry_admin_hook_hook_log_path(@hook, @hook_log), method: :post, class: "btn btn-default float-right prepend-left-10" = render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log } - diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 029efadd75d..a74e052707f 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -39,6 +39,10 @@ %strong= email.email = link_to remove_email_admin_user_path(@user, email), data: { confirm: "Are you sure you want to remove #{email.email}?" }, method: :delete, class: "btn-sm btn btn-remove float-right", title: 'Remove secondary email', id: "remove_email_#{email.id}" do %i.fa.fa-times + %li + %span.light ID: + %strong + = @user.id %li.two-factor-status %span.light Two-factor Authentication: diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml index 8ca9fb4512e..30d7b21b1b8 100644 --- a/app/views/award_emoji/_awards_block.html.haml +++ b/app/views/award_emoji/_awards_block.html.haml @@ -3,7 +3,7 @@ .awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } } - awards_sort(grouped_emojis).each do |emoji, awards| %button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button", - class: [(award_state_class(awardable, awards, current_user)), (award_user_authored_class(emoji) if user_authored)], + class: [(award_state_class(awardable, awards, current_user))], data: { placement: "bottom", title: award_user_list(awards, current_user) } } = emoji_icon(emoji) %span.award-control-text.js-counter @@ -13,7 +13,6 @@ .award-menu-holder.js-award-holder %button.btn.award-control.has-tooltip.js-add-award{ type: 'button', 'aria-label': _('Add reaction'), - class: ("js-user-authored" if user_authored), data: { title: _('Add reaction'), placement: "bottom" } } %span{ class: "award-control-icon award-control-icon-neutral" }= custom_icon('emoji_slightly_smiling_face') %span{ class: "award-control-icon award-control-icon-positive" }= custom_icon('emoji_smiley') diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml index 13f96b9747c..c26eb873718 100644 --- a/app/views/ci/runner/_how_to_setup_runner.html.haml +++ b/app/views/ci/runner/_how_to_setup_runner.html.haml @@ -1,6 +1,6 @@ - link = link_to _("Install GitLab Runner"), 'https://docs.gitlab.com/runner/install/', target: '_blank' .append-bottom-10 - %h4= _("Setup a #{type} Runner manually") + %h4= _("Setup a %{type} Runner manually") % { type: type } %ol %li diff --git a/app/views/ci/status/_dropdown_graph_badge.html.haml b/app/views/ci/status/_dropdown_graph_badge.html.haml index 8b0463db000..9de9143e8b1 100644 --- a/app/views/ci/status/_dropdown_graph_badge.html.haml +++ b/app/views/ci/status/_dropdown_graph_badge.html.haml @@ -6,12 +6,12 @@ - tooltip = "#{subject.name} - #{status.status_tooltip}" - if status.has_details? - = link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item', data: { toggle: 'tooltip', title: tooltip, html: 'true', container: 'body' } do + = link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do %span{ class: klass }= sprite_icon(status.icon) %span.ci-build-text= subject.name - else - .menu-item.mini-pipeline-graph-dropdown-item{ data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' } } + .menu-item.mini-pipeline-graph-dropdown-item{ data: { toggle: 'tooltip', title: tooltip, container: 'body' } } %span{ class: klass }= sprite_icon(status.icon) %span.ci-build-text= subject.name diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index f5f621507b8..b6424df55cd 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -5,7 +5,7 @@ .nav-controls = render 'shared/milestones_sort_dropdown' - - if can?(current_user, :admin_milestones, @group) + - if can?(current_user, :admin_milestone, @group) = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new" .milestones diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml index f0d1e837317..f4a29ed18dc 100644 --- a/app/views/import/_githubish_status.html.haml +++ b/app/views/import/_githubish_status.html.haml @@ -45,7 +45,7 @@ = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true %span.input-group-prepend .input-group-text / - = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true + = text_field_tag :path, sanitize_project_name(repo.name), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do = has_ci_cd_only_params? ? _('Connect') : _('Import') diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index a75b7aa9dd2..3b1b5e55302 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -63,7 +63,7 @@ = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true %span.input-group-prepend .input-group-text / - = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true + = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true %td.import-actions.job-status = button_tag class: 'btn btn-import js-add-to-import' do = _('Import') diff --git a/app/views/import/bitbucket_server/status.html.haml b/app/views/import/bitbucket_server/status.html.haml index 3d05a5e696f..ae09e0dfa18 100644 --- a/app/views/import/bitbucket_server/status.html.haml +++ b/app/views/import/bitbucket_server/status.html.haml @@ -61,7 +61,7 @@ = text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true %span.input-group-prepend .input-group-text / - = text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true + = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true %td.import-actions.job-status = button_tag class: 'btn btn-import js-add-to-import' do Import diff --git a/app/views/projects/_issuable_by_email.html.haml b/app/views/projects/_issuable_by_email.html.haml index 22adf5b4008..d59191a6f87 100644 --- a/app/views/projects/_issuable_by_email.html.haml +++ b/app/views/projects/_issuable_by_email.html.haml @@ -19,9 +19,16 @@ = text_field_tag :issuable_email, email, class: "monospace js-select-on-focus form-control", readonly: true .input-group-append = clipboard_button(target: '#issuable_email', class: 'btn btn-clipboard input-group-text btn-transparent d-none d-sm-block') + + - if issuable_type == 'issue' + - enter_title_text = _('Enter the issue title') + - enter_description_text = _('Enter the issue description') + - else + - enter_title_text = _('Enter the merge request title') + - enter_description_text = _('Enter the merge request description') = mail_to email, class: 'btn btn-clipboard btn-transparent', - subject: _("Enter the #{name} title"), - body: _("Enter the #{name} description"), + subject: enter_title_text, + body: enter_description_text, title: _('Send email'), data: { toggle: 'tooltip', placement: 'bottom' } do = sprite_icon('mail') diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml index 8f8eb2c3d5a..6ed65d07202 100644 --- a/app/views/projects/commits/_commit_list.html.haml +++ b/app/views/projects/commits/_commit_list.html.haml @@ -1,9 +1,10 @@ -- commits, hidden = limited_commits(@commits) +- commits = @commits +- hidden = @hidden_commit_count - commits = Commit.decorate(commits, @project) .card .card-header - Commits (#{@commits.count}) + Commits (#{@total_commit_count}) - if hidden > 0 %ul.content-list - commits.each do |commit| diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index ac6852751be..ec05ff50f25 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -2,7 +2,8 @@ - project = local_assigns.fetch(:project) { merge_request&.project } - ref = local_assigns.fetch(:ref) { merge_request&.source_branch } -- commits, hidden = limited_commits(@commits) +- commits = @commits +- hidden = @hidden_commit_count - commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits| %li.commit-header.js-commit-header{ data: { day: day } } diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml index cd0fb21f8a7..ffdca500abe 100644 --- a/app/views/projects/diffs/_line.html.haml +++ b/app/views/projects/diffs/_line.html.haml @@ -32,9 +32,9 @@ %a{ href: "##{line_code}", data: { linenumber: link_text } } %td.line_content.noteable_line{ class: type }< - if email - %pre= line.text + %pre= line.rich_text - else - = diff_line_content(line.text) + = diff_line_content(line.rich_text) - if line_discussions&.any? - discussion_expanded = local_assigns.fetch(:discussion_expanded, line_discussions.any?(&:expanded?)) diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index 1f0ca211074..e47361354f3 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -24,7 +24,7 @@ - discussion_left = discussions_left.try(:first) - if discussion_left && discussion_left.resolvable? %diff-note-avatars{ "discussion-id" => discussion_left.id } - %td.line_content.parallel.noteable_line.left-side{ id: left_line_code, class: left.type }= diff_line_content(left.text) + %td.line_content.parallel.noteable_line.left-side{ id: left_line_code, class: left.type }= diff_line_content(left.rich_text) - else %td.old_line.diff-line-num.empty-cell %td.line_content.parallel.left-side @@ -45,7 +45,7 @@ - discussion_right = discussions_right.try(:first) - if discussion_right && discussion_right.resolvable? %diff-note-avatars{ "discussion-id" => discussion_right.id } - %td.line_content.parallel.noteable_line.right-side{ id: right_line_code, class: right.type }= diff_line_content(right.text) + %td.line_content.parallel.noteable_line.right-side{ id: right_line_code, class: right.type }= diff_line_content(right.rich_text) - else %td.old_line.diff-line-num.empty-cell %td.line_content.parallel.right-side diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 30544dde451..e37a444c1c9 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -78,7 +78,7 @@ = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "sharing-permissions-form" }, authenticity_token: true do |f| %input{ name: 'update_section', type: 'hidden', value: 'js-shared-permissions' } -# haml-lint:disable InlineJavaScript - %script.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data(@project) + %script.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data_json(@project) .js-project-permissions-form = f.submit 'Save changes', class: "btn btn-save" diff --git a/app/views/projects/hook_logs/show.html.haml b/app/views/projects/hook_logs/show.html.haml index e51efa85df0..bd8ca5e7d70 100644 --- a/app/views/projects/hook_logs/show.html.haml +++ b/app/views/projects/hook_logs/show.html.haml @@ -4,6 +4,6 @@ Request details .col-lg-9 - = link_to 'Resend Request', retry_project_hook_hook_log_path(@project, @hook, @hook_log), class: "btn btn-default float-right prepend-left-10" + = link_to 'Resend Request', retry_project_hook_hook_log_path(@project, @hook, @hook_log), method: :post, class: "btn btn-default float-right prepend-left-10" = render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log } diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml index 86b2b8bf2f7..acc1e17b811 100644 --- a/app/views/projects/jobs/_sidebar.html.haml +++ b/app/views/projects/jobs/_sidebar.html.haml @@ -82,7 +82,7 @@ - builds.select{|build| build.status == build_status}.each do |build| .build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } } - tooltip = sanitize(build.tooltip_message.dup) - = link_to(project_job_path(@project, build), data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' }) do + = link_to(project_job_path(@project, build), data: { toggle: 'tooltip', title: tooltip, container: 'body' }) do = sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right') %span{ class: "ci-status-icon-#{build.status}" } = ci_icon_for_status(build.status) diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml index 62dd21ef6e0..d3871453b9f 100644 --- a/app/views/projects/merge_requests/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/_how_to_merge.html.haml @@ -1,5 +1,5 @@ #modal_merge_info.modal{ tabindex: '-1' } - .modal-dialog + .modal-dialog.modal-lg .modal-content .modal-header %h3.modal-title Check out, review, and merge locally diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml index f7a5d85500f..d5c4134dee2 100644 --- a/app/views/projects/merge_requests/creations/_new_submit.html.haml +++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml @@ -33,7 +33,7 @@ %li.commits-tab.new-tab = link_to url_for(safe_params), data: {target: 'div#commits', action: 'new', toggle: 'tabvue'} do Commits - %span.badge.badge-pill= @commits.size + %span.badge.badge-pill= @total_commit_count - if @pipelines.any? %li.builds-tab = link_to url_for(safe_params.merge(action: 'pipelines')), data: {target: 'div#pipelines', action: 'pipelines', toggle: 'tabvue'} do diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 2a9e20c2caa..0a684f9016a 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -43,18 +43,7 @@ - else = link_to 'Reopen milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped" - %button.js-delete-milestone-button.btn.btn-grouped.btn-danger{ data: { toggle: 'modal', - target: '#delete-milestone-modal', - milestone_id: @milestone.id, - milestone_title: markdown_field(@milestone, :title), - milestone_url: project_milestone_path(@project, @milestone), - milestone_issue_count: @milestone.issues.count, - milestone_merge_request_count: @milestone.merge_requests.count }, - disabled: true } - = _('Delete') - = icon('spin spinner', class: 'js-loading-icon hidden' ) - - #delete-milestone-modal + = render 'shared/milestones/delete_button' %a.btn.btn-default.btn-grouped.float-right.d-block.d-sm-none.js-sidebar-toggle{ href: "#" } = icon('angle-double-left') diff --git a/app/views/projects/mirrors/_instructions.html.haml b/app/views/projects/mirrors/_instructions.html.haml index e051f9e6331..35a6885318a 100644 --- a/app/views/projects/mirrors/_instructions.html.haml +++ b/app/views/projects/mirrors/_instructions.html.haml @@ -4,7 +4,9 @@ = _('The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>.').html_safe %li= _('Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>.').html_safe - %li= _("The update action will time out after #{import_will_timeout_message(Gitlab.config.gitlab_shell.git_timeout)} minutes. For big repositories, use a clone/push combination.") + %li + - minutes = Gitlab.config.gitlab_shell.git_timeout / 60 + = _("The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination.") % { number_of_minutes: minutes } %li= _('The Git LFS objects will <strong>not</strong> be synced.').html_safe %li = _('This user will be the author of all events in the activity feed that are the result of an update, diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml index b4fe1cabdfd..e9008d60098 100644 --- a/app/views/projects/notes/_actions.html.haml +++ b/app/views/projects/notes/_actions.html.haml @@ -40,7 +40,7 @@ - if note.emoji_awardable? - user_authored = note.user_authored?(current_user) .note-actions-item - = button_tag title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored} has-tooltip btn btn-transparent", data: { position: 'right', container: 'body' } do + = button_tag title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji} has-tooltip btn btn-transparent", data: { position: 'right', container: 'body' } do = icon('spinner spin') %span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face') %span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley') diff --git a/app/views/shared/milestones/_delete_button.html.haml b/app/views/shared/milestones/_delete_button.html.haml new file mode 100644 index 00000000000..e236c24b088 --- /dev/null +++ b/app/views/shared/milestones/_delete_button.html.haml @@ -0,0 +1,14 @@ +- milestone_url = @milestone.project_milestone? ? project_milestone_path(@project, @milestone) : group_milestone_path(@group, @milestone) + +%button.js-delete-milestone-button.btn.btn-grouped.btn-danger{ data: { toggle: 'modal', + target: '#delete-milestone-modal', + milestone_id: @milestone.id, + milestone_title: markdown_field(@milestone, :title), + milestone_url: milestone_url, + milestone_issue_count: @milestone.issues.count, + milestone_merge_request_count: @milestone.merge_requests.count }, + disabled: true } + = _('Delete') + = icon('spin spinner', class: 'js-loading-icon hidden' ) + +#delete-milestone-modal diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index c559945a9c9..3dd2842be4f 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -16,6 +16,9 @@ = milestone_date_range(milestone) %div = render('shared/milestone_expired', milestone: milestone) + - if milestone.group_milestone? + .label-badge.label-badge-blue.d-inline-block + = milestone.group.full_name - if milestone.legacy_group_milestone? .projects - milestone.milestones.each do |milestone| @@ -49,7 +52,7 @@ - unless milestone.active? = link_to 'Reopen Milestone', project_milestone_path(@project, milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" - if @group - - if can?(current_user, :admin_milestones, @group) + - if can?(current_user, :admin_milestone, @group) - if milestone.closed? = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen" - else diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 320e3788a0f..0499b04a482 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -23,7 +23,7 @@ = milestone_date_range(milestone) - if group .float-right - - if can?(current_user, :admin_milestones, group) + - if can?(current_user, :admin_milestone, group) - if milestone.group_milestone? = link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do Edit @@ -32,6 +32,9 @@ - else = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" + - unless is_dynamic_milestone + = render 'shared/milestones/delete_button' + = render 'shared/milestones/deprecation_message' if is_dynamic_milestone .detail-page-description.milestone-detail diff --git a/app/views/snippets/notes/_actions.html.haml b/app/views/snippets/notes/_actions.html.haml index 3a50324770d..e1f7ee80ebb 100644 --- a/app/views/snippets/notes/_actions.html.haml +++ b/app/views/snippets/notes/_actions.html.haml @@ -2,7 +2,7 @@ - if note.emoji_awardable? - user_authored = note.user_authored?(current_user) .note-actions-item - = link_to '#', title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored} has-tooltip", data: { position: 'right' } do + = link_to '#', title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip", data: { position: 'right' } do = icon('spinner spin') %span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face') %span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley') diff --git a/changelogs/unreleased/43096-controller-projects-issuescontroller-referenced_merge_requests-json-executes-more-than-100-sql-queries.yml b/changelogs/unreleased/43096-controller-projects-issuescontroller-referenced_merge_requests-json-executes-more-than-100-sql-queries.yml new file mode 100644 index 00000000000..f4744c868ef --- /dev/null +++ b/changelogs/unreleased/43096-controller-projects-issuescontroller-referenced_merge_requests-json-executes-more-than-100-sql-queries.yml @@ -0,0 +1,5 @@ +--- +title: Improve performance when fetching related merge requests for an issue +merge_request: 21237 +author: +type: performance diff --git a/changelogs/unreleased/43625-increase-modal-checkout.yml b/changelogs/unreleased/43625-increase-modal-checkout.yml new file mode 100644 index 00000000000..877550924c1 --- /dev/null +++ b/changelogs/unreleased/43625-increase-modal-checkout.yml @@ -0,0 +1,5 @@ +--- +title: Increase width of checkout branch modal box +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/49110-update-mr-widget-styles.yml b/changelogs/unreleased/49110-update-mr-widget-styles.yml new file mode 100644 index 00000000000..e54866a0908 --- /dev/null +++ b/changelogs/unreleased/49110-update-mr-widget-styles.yml @@ -0,0 +1,5 @@ +--- +title: Truncate branch names and update "commits behind" text in MR page +merge_request: 21206 +author: +type: changed diff --git a/changelogs/unreleased/49292-add-group-name-badge-under-milestone.yml b/changelogs/unreleased/49292-add-group-name-badge-under-milestone.yml new file mode 100644 index 00000000000..69089cfe357 --- /dev/null +++ b/changelogs/unreleased/49292-add-group-name-badge-under-milestone.yml @@ -0,0 +1,5 @@ +--- +title: Add group name badge under group milestone +merge_request: 21384 +author: +type: added diff --git a/changelogs/unreleased/50101-env-block.yml b/changelogs/unreleased/50101-env-block.yml new file mode 100644 index 00000000000..11e603e7a79 --- /dev/null +++ b/changelogs/unreleased/50101-env-block.yml @@ -0,0 +1,5 @@ +--- +title: Creates vue component for environments information in job log view +merge_request: +author: +type: other diff --git a/changelogs/unreleased/50345-hashed-storage-feature-flag.yml b/changelogs/unreleased/50345-hashed-storage-feature-flag.yml new file mode 100644 index 00000000000..4c5182b843b --- /dev/null +++ b/changelogs/unreleased/50345-hashed-storage-feature-flag.yml @@ -0,0 +1,5 @@ +--- +title: Feature flag to disable Hashed Storage migration when renaming a repository +merge_request: 21291 +author: +type: added diff --git a/changelogs/unreleased/50441-high-number-of-statement-timeouts-in-groupdestroyworker-due-to-sitestatistics.yml b/changelogs/unreleased/50441-high-number-of-statement-timeouts-in-groupdestroyworker-due-to-sitestatistics.yml new file mode 100644 index 00000000000..3e360f8d6bb --- /dev/null +++ b/changelogs/unreleased/50441-high-number-of-statement-timeouts-in-groupdestroyworker-due-to-sitestatistics.yml @@ -0,0 +1,5 @@ +--- +title: Removing a group no longer triggers hooks for project deletion twice +merge_request: 21366 +author: +type: fixed diff --git a/changelogs/unreleased/50584-fix-ide-commit-twice.yml b/changelogs/unreleased/50584-fix-ide-commit-twice.yml new file mode 100644 index 00000000000..92b292cf4ab --- /dev/null +++ b/changelogs/unreleased/50584-fix-ide-commit-twice.yml @@ -0,0 +1,5 @@ +--- +title: Fix Web IDE unable to commit to same file twice +merge_request: 21372 +author: +type: fixed diff --git a/changelogs/unreleased/50801-error-getting-performance-bar-results-for-uuid.yml b/changelogs/unreleased/50801-error-getting-performance-bar-results-for-uuid.yml new file mode 100644 index 00000000000..6e57a215367 --- /dev/null +++ b/changelogs/unreleased/50801-error-getting-performance-bar-results-for-uuid.yml @@ -0,0 +1,5 @@ +--- +title: Don't show flash messages for performance bar errors +merge_request: 21411 +author: +type: other diff --git a/changelogs/unreleased/6028-show-generic-percent-stacked-progress-bar.yml b/changelogs/unreleased/6028-show-generic-percent-stacked-progress-bar.yml new file mode 100644 index 00000000000..94098dd0144 --- /dev/null +++ b/changelogs/unreleased/6028-show-generic-percent-stacked-progress-bar.yml @@ -0,0 +1,6 @@ +--- +title: Show '< 1%' when percent value evaluated is less than 1 on Stacked Progress + Bar +merge_request: 21306 +author: +type: fixed diff --git a/changelogs/unreleased/api-protected-tags.yml b/changelogs/unreleased/api-protected-tags.yml new file mode 100644 index 00000000000..6e7ecf24b6e --- /dev/null +++ b/changelogs/unreleased/api-protected-tags.yml @@ -0,0 +1,5 @@ +--- +title: 'API: Protected tags' +merge_request: 14986 +author: Robert Schilling +type: added diff --git a/changelogs/unreleased/bvl-add-galician.yml b/changelogs/unreleased/bvl-add-galician.yml new file mode 100644 index 00000000000..e7035901ace --- /dev/null +++ b/changelogs/unreleased/bvl-add-galician.yml @@ -0,0 +1,5 @@ +--- +title: Add Galician as an available language. +merge_request: 21202 +author: +type: added diff --git a/changelogs/unreleased/ccr-43283_allow_author_upvote.yml b/changelogs/unreleased/ccr-43283_allow_author_upvote.yml new file mode 100644 index 00000000000..12ef6e3f790 --- /dev/null +++ b/changelogs/unreleased/ccr-43283_allow_author_upvote.yml @@ -0,0 +1,5 @@ +--- +title: Allow author to vote on their own issue and MRs +merge_request: 21203 +author: +type: changed diff --git a/changelogs/unreleased/ccr-48800-ping_for_boards.yml b/changelogs/unreleased/ccr-48800-ping_for_boards.yml new file mode 100644 index 00000000000..c08578cddba --- /dev/null +++ b/changelogs/unreleased/ccr-48800-ping_for_boards.yml @@ -0,0 +1,6 @@ +--- +title: Adds count for different board list types (label lists, assignee lists, and + milestone lists) to usage statistics. +merge_request: 21208 +author: +type: changed diff --git a/changelogs/unreleased/expose-users-id-in-admin-users-show-page.yml b/changelogs/unreleased/expose-users-id-in-admin-users-show-page.yml new file mode 100644 index 00000000000..0b8ae527214 --- /dev/null +++ b/changelogs/unreleased/expose-users-id-in-admin-users-show-page.yml @@ -0,0 +1,5 @@ +--- +title: Expose user's id in /admin/users/ show page +merge_request: +author: Eva Kadlecova +type: changed diff --git a/changelogs/unreleased/fix-api-group-createdat.yml b/changelogs/unreleased/fix-api-group-createdat.yml new file mode 100644 index 00000000000..e628facf1bf --- /dev/null +++ b/changelogs/unreleased/fix-api-group-createdat.yml @@ -0,0 +1,5 @@ +--- +title: Allow date parameters on Issues, Notes, and Discussions API for group owners +merge_request: 21342 +author: Florent Dubois +type: fixed diff --git a/changelogs/unreleased/fix-mr-title-fallback-logic.yml b/changelogs/unreleased/fix-mr-title-fallback-logic.yml new file mode 100644 index 00000000000..5056c38573b --- /dev/null +++ b/changelogs/unreleased/fix-mr-title-fallback-logic.yml @@ -0,0 +1,5 @@ +--- +title: Fix fallback logic for automatic MR title assignment +merge_request: 20930 +author: Franz Liedke +type: fixed diff --git a/changelogs/unreleased/fix_emojis_cutting_and_regressions.yml b/changelogs/unreleased/fix_emojis_cutting_and_regressions.yml new file mode 100644 index 00000000000..a9c1b88a61c --- /dev/null +++ b/changelogs/unreleased/fix_emojis_cutting_and_regressions.yml @@ -0,0 +1,5 @@ +--- +title: Fix Emojis cutting in the right way +merge_request: +author: Alexander Popov +type: fixed diff --git a/changelogs/unreleased/issue_36138.yml b/changelogs/unreleased/issue_36138.yml new file mode 100644 index 00000000000..2fb2eea65f5 --- /dev/null +++ b/changelogs/unreleased/issue_36138.yml @@ -0,0 +1,5 @@ +--- +title: Allow to delete group milestones +merge_request: +author: +type: added diff --git a/changelogs/unreleased/jprovazn-fix-form-uploads.yml b/changelogs/unreleased/jprovazn-fix-form-uploads.yml new file mode 100644 index 00000000000..8bcee335e93 --- /dev/null +++ b/changelogs/unreleased/jprovazn-fix-form-uploads.yml @@ -0,0 +1,5 @@ +--- +title: Accept upload files in public/uplaods/tmp when using accelerated uploads. +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/rails5-fix-import-merge-request-creator.yml b/changelogs/unreleased/rails5-fix-import-merge-request-creator.yml new file mode 100644 index 00000000000..661bd748333 --- /dev/null +++ b/changelogs/unreleased/rails5-fix-import-merge-request-creator.yml @@ -0,0 +1,5 @@ +--- +title: 'Rails5: fix can''t quote ActiveSupport::HashWithIndifferentAccess' +merge_request: 21397 +author: Jasper Maes +type: other diff --git a/changelogs/unreleased/rails5-silence-stream.yml b/changelogs/unreleased/rails5-silence-stream.yml new file mode 100644 index 00000000000..df4fd14a077 --- /dev/null +++ b/changelogs/unreleased/rails5-silence-stream.yml @@ -0,0 +1,5 @@ +--- +title: 'Rails 5: replace removed silence_stream' +merge_request: 21387 +author: Jasper Maes +type: other diff --git a/changelogs/unreleased/rails5-update-gemfile-lock.yml b/changelogs/unreleased/rails5-update-gemfile-lock.yml new file mode 100644 index 00000000000..3891b16e2b8 --- /dev/null +++ b/changelogs/unreleased/rails5-update-gemfile-lock.yml @@ -0,0 +1,5 @@ +--- +title: Rails5 update Gemfile.rails5.lock +merge_request: 21388 +author: Jasper Maes +type: other diff --git a/changelogs/unreleased/security-49085-persistent-xss-rendering.yml b/changelogs/unreleased/security-49085-persistent-xss-rendering.yml new file mode 100644 index 00000000000..dc15d356c1c --- /dev/null +++ b/changelogs/unreleased/security-49085-persistent-xss-rendering.yml @@ -0,0 +1,5 @@ +--- +title: Fixed persistent XSS rendering/escaping of diff location lines +merge_request: +author: +type: security diff --git a/changelogs/unreleased/sh-block-link-local-master.yml b/changelogs/unreleased/sh-block-link-local-master.yml new file mode 100644 index 00000000000..0a6017479af --- /dev/null +++ b/changelogs/unreleased/sh-block-link-local-master.yml @@ -0,0 +1,5 @@ +--- +title: Block link-local addresses in URLBlocker +merge_request: +author: +type: security diff --git a/changelogs/unreleased/sh-conditional-system-hook-push.yml b/changelogs/unreleased/sh-conditional-system-hook-push.yml deleted file mode 100644 index 3a1a1b3d36c..00000000000 --- a/changelogs/unreleased/sh-conditional-system-hook-push.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Eliminate unnecessary and duplicate system hook fires -merge_request: 21337 -author: -type: performance diff --git a/changelogs/unreleased/sh-fix-issue-50562.yml b/changelogs/unreleased/sh-fix-issue-50562.yml new file mode 100644 index 00000000000..a207dd28622 --- /dev/null +++ b/changelogs/unreleased/sh-fix-issue-50562.yml @@ -0,0 +1,5 @@ +--- +title: Fix remote mirrors failing if Git remotes have not been added +merge_request: 21351 +author: +type: fixed diff --git a/changelogs/unreleased/sh-insert-git-data-in-separate-transaction.yml b/changelogs/unreleased/sh-insert-git-data-in-separate-transaction.yml new file mode 100644 index 00000000000..116929b2f53 --- /dev/null +++ b/changelogs/unreleased/sh-insert-git-data-in-separate-transaction.yml @@ -0,0 +1,5 @@ +--- +title: 'Bitbucket Server importer: Eliminate most idle-in-transaction issues' +merge_request: +author: +type: performance diff --git a/changelogs/unreleased/sh-limit-commit-renderering.yml b/changelogs/unreleased/sh-limit-commit-renderering.yml new file mode 100644 index 00000000000..c44c67bcc90 --- /dev/null +++ b/changelogs/unreleased/sh-limit-commit-renderering.yml @@ -0,0 +1,5 @@ +--- +title: Speed up diff comparisons by limiting number of commit messages rendered +merge_request: 21335 +author: +type: performance diff --git a/changelogs/unreleased/sh-sanitize-project-import-names.yml b/changelogs/unreleased/sh-sanitize-project-import-names.yml new file mode 100644 index 00000000000..6e0284bda08 --- /dev/null +++ b/changelogs/unreleased/sh-sanitize-project-import-names.yml @@ -0,0 +1,5 @@ +--- +title: Use slugs for default project path and sanitize names before import +merge_request: 21367 +author: +type: fixed diff --git a/changelogs/unreleased/winh-default-status-emoji.yml b/changelogs/unreleased/winh-default-status-emoji.yml new file mode 100644 index 00000000000..00cca4db0a6 --- /dev/null +++ b/changelogs/unreleased/winh-default-status-emoji.yml @@ -0,0 +1,5 @@ +--- +title: Display default status emoji if only message is entered +merge_request: 21330 +author: +type: changed diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 7ee960970f8..fa1f79a90be 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -59,7 +59,7 @@ namespace :admin do resources :hook_logs, only: [:show] do member do - get :retry + post :retry end end end diff --git a/config/routes/group.rb b/config/routes/group.rb index 25fbb38ba87..d7313e43786 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -37,7 +37,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do post :toggle_subscription, on: :member end - resources :milestones, constraints: { id: %r{[^/]+} }, only: [:index, :show, :edit, :update, :new, :create] do + resources :milestones, constraints: { id: %r{[^/]+} } do member do get :merge_requests get :participants diff --git a/config/routes/project.rb b/config/routes/project.rb index 0220e88c819..34f49546983 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -307,7 +307,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resources :hook_logs, only: [:show] do member do - get :retry + post :retry end end end diff --git a/danger/changelog/Dangerfile b/danger/changelog/Dangerfile index a1f94dc6004..713ed95a04c 100644 --- a/danger/changelog/Dangerfile +++ b/danger/changelog/Dangerfile @@ -53,9 +53,11 @@ end changelog_needed = (gitlab.mr_labels & NO_CHANGELOG_LABELS).empty? changelog_found = git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} } +mr_title = gitlab.mr_json["title"].gsub(/^WIP: */, '') + if git.modified_files.include?("CHANGELOG.md") fail "**CHANGELOG.md was edited.** Please remove the additions and create a CHANGELOG entry.\n\n" + - format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels) + format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: mr_title, labels: presented_no_changelog_labels) end if changelog_needed @@ -63,6 +65,6 @@ if changelog_needed check_changelog(changelog_found) else warn "**[CHANGELOG missing](https://docs.gitlab.com/ce/development/changelog.html).**\n\n" + - format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels) + format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: mr_title, labels: presented_no_changelog_labels) end end diff --git a/db/migrate/20180711103851_drop_duplicate_protected_tags.rb b/db/migrate/20180711103851_drop_duplicate_protected_tags.rb new file mode 100644 index 00000000000..8fa2137551e --- /dev/null +++ b/db/migrate/20180711103851_drop_duplicate_protected_tags.rb @@ -0,0 +1,45 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class DropDuplicateProtectedTags < ActiveRecord::Migration + DOWNTIME = false + + disable_ddl_transaction! + + BATCH_SIZE = 1000 + + class Project < ActiveRecord::Base + self.table_name = 'projects' + + include ::EachBatch + end + + class ProtectedTag < ActiveRecord::Base + self.table_name = 'protected_tags' + end + + def up + Project.each_batch(of: BATCH_SIZE) do |projects| + ids = ProtectedTag + .where(project_id: projects) + .group(:name, :project_id) + .select('max(id)') + + tags = ProtectedTag + .where(project_id: projects) + .where.not(id: ids) + + if Gitlab::Database.postgresql? + tags.delete_all + else + # Workaround needed for MySQL + sql = "SELECT id FROM (#{tags.to_sql}) protected_tags" + + ProtectedTag.where("id IN (#{sql})").delete_all # rubocop:disable GitlabSecurity/SqlInjection + end + end + end + + def down + end +end diff --git a/db/migrate/20180711103922_add_protected_tags_index.rb b/db/migrate/20180711103922_add_protected_tags_index.rb new file mode 100644 index 00000000000..7ed2258ebaf --- /dev/null +++ b/db/migrate/20180711103922_add_protected_tags_index.rb @@ -0,0 +1,18 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddProtectedTagsIndex < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :protected_tags, [:project_id, :name], unique: true + end + + def down + remove_concurrent_index :protected_tags, [:project_id, :name] + end +end diff --git a/db/migrate/20180815175440_add_index_on_list_type.rb b/db/migrate/20180815175440_add_index_on_list_type.rb new file mode 100644 index 00000000000..aad805e436e --- /dev/null +++ b/db/migrate/20180815175440_add_index_on_list_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +class AddIndexOnListType < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :lists, :list_type + end + + def down + remove_concurrent_index :lists, :list_type + end +end diff --git a/db/post_migrate/20180826111825_recalculate_site_statistics.rb b/db/post_migrate/20180826111825_recalculate_site_statistics.rb new file mode 100644 index 00000000000..741035a444f --- /dev/null +++ b/db/post_migrate/20180826111825_recalculate_site_statistics.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class RecalculateSiteStatistics < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + transaction do + execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967 + + execute("UPDATE site_statistics SET repositories_count = (SELECT COUNT(*) FROM projects)") + end + + transaction do + execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967 + + execute("UPDATE site_statistics SET wikis_count = (SELECT COUNT(*) FROM project_features WHERE wiki_access_level != 0)") + end + end + + def down + # No downside in keeping the counter up-to-date + end +end diff --git a/db/schema.rb b/db/schema.rb index 6168a1be29e..cb8f90efded 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180816193530) do +ActiveRecord::Schema.define(version: 20180826111825) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1135,6 +1135,7 @@ ActiveRecord::Schema.define(version: 20180816193530) do add_index "lists", ["board_id", "label_id"], name: "index_lists_on_board_id_and_label_id", unique: true, using: :btree add_index "lists", ["label_id"], name: "index_lists_on_label_id", using: :btree + add_index "lists", ["list_type"], name: "index_lists_on_list_type", using: :btree create_table "members", force: :cascade do |t| t.integer "access_level", null: false @@ -1740,6 +1741,7 @@ ActiveRecord::Schema.define(version: 20180816193530) do t.datetime "updated_at", null: false end + add_index "protected_tags", ["project_id", "name"], name: "index_protected_tags_on_project_id_and_name", unique: true, using: :btree add_index "protected_tags", ["project_id"], name: "index_protected_tags_on_project_id", using: :btree create_table "push_event_payloads", id: false, force: :cascade do |t| diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/img/gitlab_ou.png b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/img/gitlab_ou.png Binary files differindex 11ce324f938..223fd0ac401 100644 --- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/img/gitlab_ou.png +++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/img/gitlab_ou.png diff --git a/doc/administration/auth/img/crowd_application.png b/doc/administration/auth/img/crowd_application.png Binary files differindex 7deea9dac8e..5029a005667 100644 --- a/doc/administration/auth/img/crowd_application.png +++ b/doc/administration/auth/img/crowd_application.png diff --git a/doc/administration/auth/img/crowd_application_authorisation.png b/doc/administration/auth/img/crowd_application_authorisation.png Binary files differindex 70339891b34..0e0bde1344b 100644 --- a/doc/administration/auth/img/crowd_application_authorisation.png +++ b/doc/administration/auth/img/crowd_application_authorisation.png diff --git a/doc/administration/img/circuitbreaker_config.png b/doc/administration/img/circuitbreaker_config.png Binary files differindex 693b2ee9c69..20233276055 100644 --- a/doc/administration/img/circuitbreaker_config.png +++ b/doc/administration/img/circuitbreaker_config.png diff --git a/doc/administration/img/custom_hooks_error_msg.png b/doc/administration/img/custom_hooks_error_msg.png Binary files differindex 1b3277bef16..845f0de19ce 100644 --- a/doc/administration/img/custom_hooks_error_msg.png +++ b/doc/administration/img/custom_hooks_error_msg.png diff --git a/doc/administration/img/failing_storage.png b/doc/administration/img/failing_storage.png Binary files differindex 82b393a58b2..652d7dcb5d7 100644 --- a/doc/administration/img/failing_storage.png +++ b/doc/administration/img/failing_storage.png diff --git a/doc/administration/img/integration/plantuml-example.png b/doc/administration/img/integration/plantuml-example.png Binary files differindex cb64eca1a8a..3e0d6389cbd 100644 --- a/doc/administration/img/integration/plantuml-example.png +++ b/doc/administration/img/integration/plantuml-example.png diff --git a/doc/administration/img/repository_storages_admin_ui.png b/doc/administration/img/repository_storages_admin_ui.png Binary files differindex 036e708cdac..5f1b4936704 100644 --- a/doc/administration/img/repository_storages_admin_ui.png +++ b/doc/administration/img/repository_storages_admin_ui.png diff --git a/doc/administration/monitoring/performance/img/grafana_dashboard_import.png b/doc/administration/monitoring/performance/img/grafana_dashboard_import.png Binary files differindex 7761ea00522..fd639ee0eb8 100644 --- a/doc/administration/monitoring/performance/img/grafana_dashboard_import.png +++ b/doc/administration/monitoring/performance/img/grafana_dashboard_import.png diff --git a/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png b/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png Binary files differindex 3e749eb8f9d..a98e0ed1e7d 100644 --- a/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png +++ b/doc/administration/monitoring/performance/img/grafana_data_source_configuration.png diff --git a/doc/administration/monitoring/performance/img/grafana_data_source_empty.png b/doc/administration/monitoring/performance/img/grafana_data_source_empty.png Binary files differindex 33fcaaaef64..549ada8343e 100644 --- a/doc/administration/monitoring/performance/img/grafana_data_source_empty.png +++ b/doc/administration/monitoring/performance/img/grafana_data_source_empty.png diff --git a/doc/administration/monitoring/performance/img/grafana_save_icon.png b/doc/administration/monitoring/performance/img/grafana_save_icon.png Binary files differindex c18f2147e9d..68a071f5ae2 100644 --- a/doc/administration/monitoring/performance/img/grafana_save_icon.png +++ b/doc/administration/monitoring/performance/img/grafana_save_icon.png diff --git a/doc/administration/monitoring/performance/img/performance_bar.png b/doc/administration/monitoring/performance/img/performance_bar.png Binary files differindex 48212f6276a..2bf686f9017 100644 --- a/doc/administration/monitoring/performance/img/performance_bar.png +++ b/doc/administration/monitoring/performance/img/performance_bar.png diff --git a/doc/administration/monitoring/performance/img/performance_bar_configuration_settings.png b/doc/administration/monitoring/performance/img/performance_bar_configuration_settings.png Binary files differindex 2d64ef8c5fc..fafc50cd000 100644 --- a/doc/administration/monitoring/performance/img/performance_bar_configuration_settings.png +++ b/doc/administration/monitoring/performance/img/performance_bar_configuration_settings.png diff --git a/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png b/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png Binary files differindex 52176df9ecd..7af6d401d1d 100644 --- a/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png +++ b/doc/administration/monitoring/performance/img/performance_bar_gitaly_calls.png diff --git a/doc/administration/monitoring/performance/img/performance_bar_line_profiling.png b/doc/administration/monitoring/performance/img/performance_bar_line_profiling.png Binary files differindex 7868e2c46d1..a55ce753101 100644 --- a/doc/administration/monitoring/performance/img/performance_bar_line_profiling.png +++ b/doc/administration/monitoring/performance/img/performance_bar_line_profiling.png diff --git a/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png b/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png Binary files differindex 372ae021f6b..b3219b4fa94 100644 --- a/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png +++ b/doc/administration/monitoring/performance/img/performance_bar_sql_queries.png diff --git a/doc/administration/monitoring/performance/img/request_profile_result.png b/doc/administration/monitoring/performance/img/request_profile_result.png Binary files differindex 8ebd74c2d3c..1b06e240fa0 100644 --- a/doc/administration/monitoring/performance/img/request_profile_result.png +++ b/doc/administration/monitoring/performance/img/request_profile_result.png diff --git a/doc/administration/monitoring/performance/img/request_profiling_token.png b/doc/administration/monitoring/performance/img/request_profiling_token.png Binary files differindex 9160407e028..8c4109c17f0 100644 --- a/doc/administration/monitoring/performance/img/request_profiling_token.png +++ b/doc/administration/monitoring/performance/img/request_profiling_token.png diff --git a/doc/administration/operations/img/sidekiq_job_throttling.png b/doc/administration/operations/img/sidekiq_job_throttling.png Binary files differindex dcf40b4bf17..abd09f3b115 100644 --- a/doc/administration/operations/img/sidekiq_job_throttling.png +++ b/doc/administration/operations/img/sidekiq_job_throttling.png diff --git a/doc/administration/operations/img/write_to_authorized_keys_setting.png b/doc/administration/operations/img/write_to_authorized_keys_setting.png Binary files differindex 232765f1917..f6227a6057b 100644 --- a/doc/administration/operations/img/write_to_authorized_keys_setting.png +++ b/doc/administration/operations/img/write_to_authorized_keys_setting.png diff --git a/doc/api/README.md b/doc/api/README.md index 45e926d3b6b..e2a6e87a2c3 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -53,6 +53,7 @@ following locations: - [Project Members](members.md) - [Project Snippets](project_snippets.md) - [Protected Branches](protected_branches.md) +- [Protected Tags](protected_tags.md) - [Repositories](repositories.md) - [Repository Files](repository_files.md) - [Runners](runners.md) diff --git a/doc/api/discussions.md b/doc/api/discussions.md index 65e2f9d6cd9..a1e1ff1419d 100644 --- a/doc/api/discussions.md +++ b/doc/api/discussions.md @@ -136,7 +136,7 @@ Parameters: | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `issue_iid` | integer | yes | The IID of an issue | | `body` | string | yes | The content of a discussion | -| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z | +| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/11/discussions?body=comment @@ -159,7 +159,7 @@ Parameters: | `discussion_id` | integer | yes | The ID of a discussion | | `note_id` | integer | yes | The ID of a discussion note | | `body` | string | yes | The content of a discussion | -| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z | +| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment @@ -342,7 +342,7 @@ Parameters: | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `snippet_id` | integer | yes | The ID of an snippet | | `body` | string | yes | The content of a discussion | -| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z | +| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/snippets/11/discussions?body=comment @@ -365,7 +365,7 @@ Parameters: | `discussion_id` | integer | yes | The ID of a discussion | | `note_id` | integer | yes | The ID of a discussion note | | `body` | string | yes | The content of a discussion | -| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z | +| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/snippets/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment @@ -601,7 +601,7 @@ Parameters: | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `merge_request_iid` | integer | yes | The IID of a merge request | | `body` | string | yes | The content of a discussion | -| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z | +| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) | | `position` | hash | no | Position when creating a diff note | | `position[base_sha]` | string | yes | Base commit SHA in the source branch | | `position[start_sha]` | string | yes | SHA referencing commit in target branch | @@ -659,7 +659,7 @@ Parameters: | `discussion_id` | integer | yes | The ID of a discussion | | `note_id` | integer | yes | The ID of a discussion note | | `body` | string | yes | The content of a discussion | -| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z | +| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment @@ -894,7 +894,7 @@ Parameters: | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `commit_id` | integer | yes | The ID of a commit | | `body` | string | yes | The content of a discussion | -| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z | +| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) | | `position` | hash | no | Position when creating a diff note | | `position[base_sha]` | string | yes | Base commit SHA in the source branch | | `position[start_sha]` | string | yes | SHA referencing commit in target branch | @@ -930,7 +930,7 @@ Parameters: | `discussion_id` | integer | yes | The ID of a discussion | | `note_id` | integer | yes | The ID of a discussion note | | `body` | string | yes | The content of a discussion | -| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z | +| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/commits/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md index 152929b7614..e396f4411e6 100644 --- a/doc/api/group_milestones.md +++ b/doc/api/group_milestones.md @@ -96,6 +96,19 @@ Parameters: - `start_date` (optional) - The start date of the milestone - `state_event` (optional) - The state event of the milestone (close|activate) +## Delete group milestone + +Only for user with developer access to the group. + +``` +DELETE /groups/:id/milestones/:milestone_id +``` + +Parameters: + +- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user +- `milestone_id` (required) - The ID of the group's milestone + ## Get all issues assigned to a single milestone Gets all issues assigned to a single group milestone. diff --git a/doc/api/issues.md b/doc/api/issues.md index 103eaa5655f..f4c0f4ea65b 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -470,7 +470,7 @@ POST /projects/:id/issues | `assignee_ids` | Array[integer] | no | The ID of the users to assign issue | | `milestone_id` | integer | no | The global ID of a milestone to assign issue | | `labels` | string | no | Comma-separated label names for an issue | -| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) | +| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project/group owner rights) | | `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` | | `merge_request_to_resolve_discussions_of` | integer | no | The IID of a merge request in which to resolve all issues. This will fill the issue with a default description and mark all discussions as resolved. When passing a description or title, these values will take precedence over the default values.| | `discussion_to_resolve` | string | no | The ID of a discussion to resolve. This will fill in the issue with a default description and mark the discussion as resolved. Use in combination with `merge_request_to_resolve_discussions_of`. | diff --git a/doc/api/notes.md b/doc/api/notes.md index c271d46688f..44940bdd9e5 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -100,7 +100,7 @@ Parameters: - `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) - `issue_id` (required) - The IID of an issue - `body` (required) - The content of a note -- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z +- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/11/notes?body=note diff --git a/doc/api/protected_tags.md b/doc/api/protected_tags.md new file mode 100644 index 00000000000..aa750e467f8 --- /dev/null +++ b/doc/api/protected_tags.md @@ -0,0 +1,128 @@ +# Protected tags API + +>**Note:** This feature was introduced in GitLab 11.3 + +**Valid access levels** + +Currently, these levels are recognized: +``` +0 => No access +30 => Developer access +40 => Maintainer access +``` + +## List protected tags + +Gets a list of protected tags from a project. +This function takes pagination parameters `page` and `per_page` to restrict the list of protected tags. + +``` +GET /projects/:id/protected_tags +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | + +```bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags' +``` + +Example response: + +```json +[ + { + "name": "release-1-0", + "create_access_levels": [ + { + "access_level": 40, + "access_level_description": "Maintainers" + } + ] + }, + ... +] +``` + +## Get a single protected tag or wildcard protected tag + +Gets a single protected tag or wildcard protected tag. +The pagination parameters `page` and `per_page` can be used to restrict the list of protected tags. + +``` +GET /projects/:id/protected_tags/:name +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | +| `name` | string | yes | The name of the tag or wildcard | + +```bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags/release-1-0' +``` + +Example response: + +```json +{ + "name": "release-1-0", + "create_access_levels": [ + { + "access_level": 40, + "access_level_description": "Maintainers" + } + ] +} +``` + +## Protect repository tags + +Protects a single repository tag or several project repository +tags using a wildcard protected tag. + +``` +POST /projects/:id/protected_tags +``` + +```bash +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags?name=*-stable&create_access_level=30' +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | +| `name` | string | yes | The name of the tag or wildcard | +| `create_access_level` | string | no | Access levels allowed to create (defaults: `40`, maintainer access level) | + +Example response: + +```json +{ + "name": "*-stable", + "create_access_levels": [ + { + "access_level": 30, + "access_level_description": "Developers + Maintainers" + } + ] +} +``` + +## Unprotect repository tags + +Unprotects the given protected tag or wildcard protected tag. + +``` +DELETE /projects/:id/protected_tags/:name +``` + +```bash +curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/5/protected_tags/*-stable' +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | +| `name` | string | yes | The name of the tag | diff --git a/doc/ci/autodeploy/img/auto_deploy_btn.png b/doc/ci/autodeploy/img/auto_deploy_btn.png Binary files differindex 25915ed1c9d..ee88e5ce8c0 100644 --- a/doc/ci/autodeploy/img/auto_deploy_btn.png +++ b/doc/ci/autodeploy/img/auto_deploy_btn.png diff --git a/doc/ci/autodeploy/img/auto_deploy_button.png b/doc/ci/autodeploy/img/auto_deploy_button.png Binary files differindex 423e76a6cda..0e84d9c57a1 100644 --- a/doc/ci/autodeploy/img/auto_deploy_button.png +++ b/doc/ci/autodeploy/img/auto_deploy_button.png diff --git a/doc/ci/autodeploy/img/auto_deploy_dropdown.png b/doc/ci/autodeploy/img/auto_deploy_dropdown.png Binary files differindex 5815937a4af..4094f8ebb4e 100644 --- a/doc/ci/autodeploy/img/auto_deploy_dropdown.png +++ b/doc/ci/autodeploy/img/auto_deploy_dropdown.png diff --git a/doc/ci/autodeploy/img/auto_monitoring.png b/doc/ci/autodeploy/img/auto_monitoring.png Binary files differindex 5661b50841b..5a11923d199 100644 --- a/doc/ci/autodeploy/img/auto_monitoring.png +++ b/doc/ci/autodeploy/img/auto_monitoring.png diff --git a/doc/ci/autodeploy/img/guide_connect_cluster.png b/doc/ci/autodeploy/img/guide_connect_cluster.png Binary files differindex b856b81a1d0..703d536f37a 100644 --- a/doc/ci/autodeploy/img/guide_connect_cluster.png +++ b/doc/ci/autodeploy/img/guide_connect_cluster.png diff --git a/doc/ci/autodeploy/img/guide_integration.png b/doc/ci/autodeploy/img/guide_integration.png Binary files differindex 723b2619ea2..ab72de2bba3 100644 --- a/doc/ci/autodeploy/img/guide_integration.png +++ b/doc/ci/autodeploy/img/guide_integration.png diff --git a/doc/ci/autodeploy/img/guide_secret.png b/doc/ci/autodeploy/img/guide_secret.png Binary files differindex 01f5aa49908..8469bee48b7 100644 --- a/doc/ci/autodeploy/img/guide_secret.png +++ b/doc/ci/autodeploy/img/guide_secret.png diff --git a/doc/ci/caching/img/clear_runners_cache.png b/doc/ci/caching/img/clear_runners_cache.png Binary files differindex e5db4a47b3e..4f1171513ad 100644 --- a/doc/ci/caching/img/clear_runners_cache.png +++ b/doc/ci/caching/img/clear_runners_cache.png diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/aws_config_window.png b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/aws_config_window.png Binary files differindex 76e0295722b..09eef98202f 100644 --- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/aws_config_window.png +++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/aws_config_window.png diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/gitlab_config.png b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/gitlab_config.png Binary files differindex 050a97d2726..71ffcdea289 100644 --- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/gitlab_config.png +++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/gitlab_config.png diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/test_pipeline_pass.png b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/test_pipeline_pass.png Binary files differindex 4ab5d5f401a..a9452577a42 100644 --- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/test_pipeline_pass.png +++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/img/test_pipeline_pass.png diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_page_empty_image.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_page_empty_image.png Binary files differindex b1406fed6b8..704d43ea52e 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_page_empty_image.png +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/container_registry_page_empty_image.png diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/deploy_keys_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/deploy_keys_page.png Binary files differindex 9aae11b8679..763ce48fa5a 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/deploy_keys_page.png +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/deploy_keys_page.png diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environment_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environment_page.png Binary files differindex a06b6d417cd..f299d6355cb 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environment_page.png +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environment_page.png diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environments_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environments_page.png Binary files differindex d357ecda7d2..9c301e1fc8c 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environments_page.png +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/environments_page.png diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipeline_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipeline_page.png Binary files differindex baf8dec499c..2f451615a3a 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipeline_page.png +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipeline_page.png diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page.png Binary files differindex d96c43bcf16..a5fd6b020d1 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page.png +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page.png diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page_deploy_button.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page_deploy_button.png Binary files differindex 997db10189f..1f605504171 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page_deploy_button.png +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/pipelines_page_deploy_button.png diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/secret_variables_page.png b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/secret_variables_page.png Binary files differindex 658c0b5bcac..b7906d49dcb 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/img/secret_variables_page.png +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/img/secret_variables_page.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png Binary files differindex 0f94ac60fee..77b05f55f88 100644 --- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png Binary files differindex 94828a20f51..04d3dc40fa5 100644 --- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png Binary files differindex 68503b392ed..63812b41c2c 100644 --- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png Binary files differindex d73140ccdd9..c0daa1a6a91 100644 --- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png Binary files differindex 38bfde0a3dd..727995f463c 100644 --- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png Binary files differindex bfe85c6a10b..50c6ca593c1 100644 --- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png diff --git a/doc/ci/img/deployments_view.png b/doc/ci/img/deployments_view.png Binary files differindex 436fed5f465..45d882b536c 100644 --- a/doc/ci/img/deployments_view.png +++ b/doc/ci/img/deployments_view.png diff --git a/doc/ci/img/environments_available.png b/doc/ci/img/environments_available.png Binary files differindex 2991a309655..7ab92838ece 100644 --- a/doc/ci/img/environments_available.png +++ b/doc/ci/img/environments_available.png diff --git a/doc/ci/img/environments_dynamic_groups.png b/doc/ci/img/environments_dynamic_groups.png Binary files differindex 45124b3d8d8..37828ccd0c1 100644 --- a/doc/ci/img/environments_dynamic_groups.png +++ b/doc/ci/img/environments_dynamic_groups.png diff --git a/doc/ci/img/environments_link_url_mr.png b/doc/ci/img/environments_link_url_mr.png Binary files differindex 7ce46063062..75d7311b862 100644 --- a/doc/ci/img/environments_link_url_mr.png +++ b/doc/ci/img/environments_link_url_mr.png diff --git a/doc/ci/img/environments_manual_action_deployments.png b/doc/ci/img/environments_manual_action_deployments.png Binary files differindex 93beaa0de54..c5959c0003e 100644 --- a/doc/ci/img/environments_manual_action_deployments.png +++ b/doc/ci/img/environments_manual_action_deployments.png diff --git a/doc/ci/img/environments_manual_action_environments.png b/doc/ci/img/environments_manual_action_environments.png Binary files differindex 9490be63f14..b2ec27cc721 100644 --- a/doc/ci/img/environments_manual_action_environments.png +++ b/doc/ci/img/environments_manual_action_environments.png diff --git a/doc/ci/img/environments_manual_action_jobs.png b/doc/ci/img/environments_manual_action_jobs.png Binary files differindex 9ae223cf77f..d948ee5da9e 100644 --- a/doc/ci/img/environments_manual_action_jobs.png +++ b/doc/ci/img/environments_manual_action_jobs.png diff --git a/doc/ci/img/environments_manual_action_pipelines.png b/doc/ci/img/environments_manual_action_pipelines.png Binary files differindex 129e44f6fb0..332850afb7f 100644 --- a/doc/ci/img/environments_manual_action_pipelines.png +++ b/doc/ci/img/environments_manual_action_pipelines.png diff --git a/doc/ci/img/environments_manual_action_single_pipeline.png b/doc/ci/img/environments_manual_action_single_pipeline.png Binary files differindex 1eeb4379eb7..8c1c0c1d993 100644 --- a/doc/ci/img/environments_manual_action_single_pipeline.png +++ b/doc/ci/img/environments_manual_action_single_pipeline.png diff --git a/doc/ci/img/environments_monitoring.png b/doc/ci/img/environments_monitoring.png Binary files differindex dcffdd1fdb8..63d272ae42a 100644 --- a/doc/ci/img/environments_monitoring.png +++ b/doc/ci/img/environments_monitoring.png diff --git a/doc/ci/img/environments_mr_review_app.png b/doc/ci/img/environments_mr_review_app.png Binary files differindex 4bb643d708f..61b7e9fe77c 100644 --- a/doc/ci/img/environments_mr_review_app.png +++ b/doc/ci/img/environments_mr_review_app.png diff --git a/doc/ci/img/environments_terminal_button_on_index.png b/doc/ci/img/environments_terminal_button_on_index.png Binary files differindex 061bb7c3c87..40110ff325f 100644 --- a/doc/ci/img/environments_terminal_button_on_index.png +++ b/doc/ci/img/environments_terminal_button_on_index.png diff --git a/doc/ci/img/environments_terminal_button_on_show.png b/doc/ci/img/environments_terminal_button_on_show.png Binary files differindex 4d24304bc93..e96ca9c9c7e 100644 --- a/doc/ci/img/environments_terminal_button_on_show.png +++ b/doc/ci/img/environments_terminal_button_on_show.png diff --git a/doc/ci/img/environments_terminal_page.png b/doc/ci/img/environments_terminal_page.png Binary files differindex fde1bf325a6..736b2d01a99 100644 --- a/doc/ci/img/environments_terminal_page.png +++ b/doc/ci/img/environments_terminal_page.png diff --git a/doc/ci/img/job_failure_reason.png b/doc/ci/img/job_failure_reason.png Binary files differindex a60ce1fb21c..d44b8e6d1be 100644 --- a/doc/ci/img/job_failure_reason.png +++ b/doc/ci/img/job_failure_reason.png diff --git a/doc/ci/img/pipelines_grouped.png b/doc/ci/img/pipelines_grouped.png Binary files differindex 06f52e03320..82814754747 100644 --- a/doc/ci/img/pipelines_grouped.png +++ b/doc/ci/img/pipelines_grouped.png diff --git a/doc/ci/img/pipelines_index.png b/doc/ci/img/pipelines_index.png Binary files differindex 3b522a9c5e4..e168e7e23df 100644 --- a/doc/ci/img/pipelines_index.png +++ b/doc/ci/img/pipelines_index.png diff --git a/doc/ci/img/pipelines_mini_graph.png b/doc/ci/img/pipelines_mini_graph.png Binary files differindex 042c8ffeef5..8656b02f60d 100644 --- a/doc/ci/img/pipelines_mini_graph.png +++ b/doc/ci/img/pipelines_mini_graph.png diff --git a/doc/ci/img/pipelines_mini_graph_simple.png b/doc/ci/img/pipelines_mini_graph_simple.png Binary files differindex eb36c09b2d4..d00a8313088 100644 --- a/doc/ci/img/pipelines_mini_graph_simple.png +++ b/doc/ci/img/pipelines_mini_graph_simple.png diff --git a/doc/ci/img/view_on_env_blob.png b/doc/ci/img/view_on_env_blob.png Binary files differindex f4fe99046f0..dd9ca40280a 100644 --- a/doc/ci/img/view_on_env_blob.png +++ b/doc/ci/img/view_on_env_blob.png diff --git a/doc/ci/img/view_on_env_mr.png b/doc/ci/img/view_on_env_mr.png Binary files differindex 47ddb40bdc1..2c0bd25a4f2 100644 --- a/doc/ci/img/view_on_env_mr.png +++ b/doc/ci/img/view_on_env_mr.png diff --git a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png Binary files differindex b59c1b6bc43..0523e62db70 100644 --- a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png +++ b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png diff --git a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png Binary files differindex f92c6df07a1..3ee5e39afc0 100644 --- a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png +++ b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png diff --git a/doc/ci/quick_start/img/build_log.png b/doc/ci/quick_start/img/build_log.png Binary files differindex 3a7248ca772..2bf0992c50e 100644 --- a/doc/ci/quick_start/img/build_log.png +++ b/doc/ci/quick_start/img/build_log.png diff --git a/doc/ci/quick_start/img/builds_status.png b/doc/ci/quick_start/img/builds_status.png Binary files differindex f829240f3b3..58978e23978 100644 --- a/doc/ci/quick_start/img/builds_status.png +++ b/doc/ci/quick_start/img/builds_status.png diff --git a/doc/ci/quick_start/img/new_commit.png b/doc/ci/quick_start/img/new_commit.png Binary files differindex b3dd848b294..507eb93ac0c 100644 --- a/doc/ci/quick_start/img/new_commit.png +++ b/doc/ci/quick_start/img/new_commit.png diff --git a/doc/ci/review_apps/img/review_apps_preview_in_mr.png b/doc/ci/review_apps/img/review_apps_preview_in_mr.png Binary files differindex 0300392f24b..7d0923f198f 100644 --- a/doc/ci/review_apps/img/review_apps_preview_in_mr.png +++ b/doc/ci/review_apps/img/review_apps_preview_in_mr.png diff --git a/doc/ci/runners/img/protected_runners_check_box.png b/doc/ci/runners/img/protected_runners_check_box.png Binary files differindex fb58498c7ce..3c47ebdec29 100644 --- a/doc/ci/runners/img/protected_runners_check_box.png +++ b/doc/ci/runners/img/protected_runners_check_box.png diff --git a/doc/ci/runners/img/shared_runner_ip_address.png b/doc/ci/runners/img/shared_runner_ip_address.png Binary files differindex 3b1542d59d3..527b4f4043d 100644 --- a/doc/ci/runners/img/shared_runner_ip_address.png +++ b/doc/ci/runners/img/shared_runner_ip_address.png diff --git a/doc/ci/runners/img/specific_runner_ip_address.png b/doc/ci/runners/img/specific_runner_ip_address.png Binary files differindex 3b4c3e9f2eb..e08663109ba 100644 --- a/doc/ci/runners/img/specific_runner_ip_address.png +++ b/doc/ci/runners/img/specific_runner_ip_address.png diff --git a/doc/ci/triggers/img/builds_page.png b/doc/ci/triggers/img/builds_page.png Binary files differindex c9cc8f308f4..14d73b140f4 100644 --- a/doc/ci/triggers/img/builds_page.png +++ b/doc/ci/triggers/img/builds_page.png diff --git a/doc/ci/triggers/img/trigger_single_build.png b/doc/ci/triggers/img/trigger_single_build.png Binary files differindex 837bbeffe9f..b760782afdc 100644 --- a/doc/ci/triggers/img/trigger_single_build.png +++ b/doc/ci/triggers/img/trigger_single_build.png diff --git a/doc/customization/branded_login_page/custom_sign_in.png b/doc/customization/branded_login_page/custom_sign_in.png Binary files differindex c0888fe1f18..03ea5281ebe 100644 --- a/doc/customization/branded_login_page/custom_sign_in.png +++ b/doc/customization/branded_login_page/custom_sign_in.png diff --git a/doc/customization/branded_page_and_email_header/appearance.png b/doc/customization/branded_page_and_email_header/appearance.png Binary files differindex abbba6f9ac9..6b79bc47005 100644 --- a/doc/customization/branded_page_and_email_header/appearance.png +++ b/doc/customization/branded_page_and_email_header/appearance.png diff --git a/doc/customization/branded_page_and_email_header/custom_brand_header.png b/doc/customization/branded_page_and_email_header/custom_brand_header.png Binary files differindex 7390f8a5e4e..d779236bbe7 100644 --- a/doc/customization/branded_page_and_email_header/custom_brand_header.png +++ b/doc/customization/branded_page_and_email_header/custom_brand_header.png diff --git a/doc/customization/branded_page_and_email_header/custom_email_header.png b/doc/customization/branded_page_and_email_header/custom_email_header.png Binary files differindex 705698ef4a8..729b166364b 100644 --- a/doc/customization/branded_page_and_email_header/custom_email_header.png +++ b/doc/customization/branded_page_and_email_header/custom_email_header.png diff --git a/doc/customization/favicon/appearance.png b/doc/customization/favicon/appearance.png Binary files differindex 6c41a05fc1f..da1002826dd 100644 --- a/doc/customization/favicon/appearance.png +++ b/doc/customization/favicon/appearance.png diff --git a/doc/customization/favicon/custom_favicon.png b/doc/customization/favicon/custom_favicon.png Binary files differindex fa1b8827a36..20dddfbea33 100644 --- a/doc/customization/favicon/custom_favicon.png +++ b/doc/customization/favicon/custom_favicon.png diff --git a/doc/customization/new_project_page/appearance_settings.png b/doc/customization/new_project_page/appearance_settings.png Binary files differindex 08eea684e14..4fcdd1caa21 100644 --- a/doc/customization/new_project_page/appearance_settings.png +++ b/doc/customization/new_project_page/appearance_settings.png diff --git a/doc/customization/new_project_page/custom_new_project_page.png b/doc/customization/new_project_page/custom_new_project_page.png Binary files differindex 662c715f193..c6f7839e9c3 100644 --- a/doc/customization/new_project_page/custom_new_project_page.png +++ b/doc/customization/new_project_page/custom_new_project_page.png diff --git a/doc/customization/new_project_page/default_new_project_page.png b/doc/customization/new_project_page/default_new_project_page.png Binary files differindex 4a0bcf09903..f5b209ac5ea 100644 --- a/doc/customization/new_project_page/default_new_project_page.png +++ b/doc/customization/new_project_page/default_new_project_page.png diff --git a/doc/development/README.md b/doc/development/README.md index ee9a9852205..20f8fa1d368 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -54,6 +54,7 @@ description: 'Learn how to contribute to GitLab.' - [Performance guidelines](performance.md) - [Merge request performance guidelines](merge_request_performance_guidelines.md) for ensuring merge requests do not negatively impact GitLab performance +- [Profiling](profiling.md) for profiling a URL ## Database guides diff --git a/doc/development/architecture.md b/doc/development/architecture.md index 3e417a44ec1..66d8a4f2f6e 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -70,7 +70,7 @@ The add-on component gitlab-shell serves repositories over SSH. It manages the S Gitaly executes git operations from gitlab-shell and the GitLab web app, and provides an API to the GitLab web app to get attributes from git (e.g. title, branches, tags, other meta data), and to get blobs (e.g. diffs, commits, files). -You may also be interested in the [production architecture of GitLab.com](https://about.gitlab.com/handbook/infrastructure/production-architecture/). +You may also be interested in the [production architecture of GitLab.com](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/). ### Installation Folder Summary diff --git a/doc/development/documentation/img/manual_build_docs.png b/doc/development/documentation/img/manual_build_docs.png Binary files differindex 615facabb5f..e366a2f7ec4 100644 --- a/doc/development/documentation/img/manual_build_docs.png +++ b/doc/development/documentation/img/manual_build_docs.png diff --git a/doc/development/fe_guide/img/boards_diagram.png b/doc/development/fe_guide/img/boards_diagram.png Binary files differindex 7a2cf972fd0..856c9b05bbf 100644 --- a/doc/development/fe_guide/img/boards_diagram.png +++ b/doc/development/fe_guide/img/boards_diagram.png diff --git a/doc/development/fe_guide/img/gl-modal.png b/doc/development/fe_guide/img/gl-modal.png Binary files differindex 47302e857bc..b2d2d637e57 100644 --- a/doc/development/fe_guide/img/gl-modal.png +++ b/doc/development/fe_guide/img/gl-modal.png diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md index 09ea8c05be6..702caacc74f 100644 --- a/doc/development/feature_flags.md +++ b/doc/development/feature_flags.md @@ -57,3 +57,15 @@ end Features that are developed and are intended to be merged behind a feature flag should not include a changelog entry. The entry should be added in the merge request removing the feature flags. + +### Specs + +In the test environment `Feature.enabled?` is stubbed to always respond to `true`, +so we make sure behavior under feature flag doesn't go untested in some non-specific +contexts. + +If you need to test the feature flag in a different state, you need to stub it with: + +```ruby +stub_feature_flags(my_feature_flag: false) +``` diff --git a/doc/development/gitlab_architecture_diagram.png b/doc/development/gitlab_architecture_diagram.png Binary files differindex 378f7384574..90e27d5462a 100644 --- a/doc/development/gitlab_architecture_diagram.png +++ b/doc/development/gitlab_architecture_diagram.png diff --git a/doc/development/img/trigger_ss1.png b/doc/development/img/trigger_ss1.png Binary files differindex ccff1009a25..addbc551f73 100644 --- a/doc/development/img/trigger_ss1.png +++ b/doc/development/img/trigger_ss1.png diff --git a/doc/development/img/trigger_ss2.png b/doc/development/img/trigger_ss2.png Binary files differindex 94dfd048793..02ef3810a59 100644 --- a/doc/development/img/trigger_ss2.png +++ b/doc/development/img/trigger_ss2.png diff --git a/doc/development/ux_guide/img/button-close--active.png b/doc/development/ux_guide/img/button-close--active.png Binary files differindex 824bfc8f31b..97a5301fb91 100644 --- a/doc/development/ux_guide/img/button-close--active.png +++ b/doc/development/ux_guide/img/button-close--active.png diff --git a/doc/development/ux_guide/img/button-close--hover.png b/doc/development/ux_guide/img/button-close--hover.png Binary files differindex 0291e121894..6b8fdf5695b 100644 --- a/doc/development/ux_guide/img/button-close--hover.png +++ b/doc/development/ux_guide/img/button-close--hover.png diff --git a/doc/development/ux_guide/img/button-close--resting.png b/doc/development/ux_guide/img/button-close--resting.png Binary files differindex 986d7174ce7..5679b51687c 100644 --- a/doc/development/ux_guide/img/button-close--resting.png +++ b/doc/development/ux_guide/img/button-close--resting.png diff --git a/doc/development/ux_guide/img/button-danger--active.png b/doc/development/ux_guide/img/button-danger--active.png Binary files differindex d3c64424b26..6a9aab0fcc2 100644 --- a/doc/development/ux_guide/img/button-danger--active.png +++ b/doc/development/ux_guide/img/button-danger--active.png diff --git a/doc/development/ux_guide/img/button-danger--hover.png b/doc/development/ux_guide/img/button-danger--hover.png Binary files differindex 8506e093306..13e21c28779 100644 --- a/doc/development/ux_guide/img/button-danger--hover.png +++ b/doc/development/ux_guide/img/button-danger--hover.png diff --git a/doc/development/ux_guide/img/button-danger--resting.png b/doc/development/ux_guide/img/button-danger--resting.png Binary files differindex 69ad6bb796b..0ff192bc463 100644 --- a/doc/development/ux_guide/img/button-danger--resting.png +++ b/doc/development/ux_guide/img/button-danger--resting.png diff --git a/doc/development/ux_guide/img/button-info--active.png b/doc/development/ux_guide/img/button-info--active.png Binary files differindex 23be20b225c..12ecdc72a31 100644 --- a/doc/development/ux_guide/img/button-info--active.png +++ b/doc/development/ux_guide/img/button-info--active.png diff --git a/doc/development/ux_guide/img/button-info--hover.png b/doc/development/ux_guide/img/button-info--hover.png Binary files differindex 4cb4e38558c..3bf93bf2b32 100644 --- a/doc/development/ux_guide/img/button-info--hover.png +++ b/doc/development/ux_guide/img/button-info--hover.png diff --git a/doc/development/ux_guide/img/button-info--resting.png b/doc/development/ux_guide/img/button-info--resting.png Binary files differindex 5883340aa83..a37a37033bf 100644 --- a/doc/development/ux_guide/img/button-info--resting.png +++ b/doc/development/ux_guide/img/button-info--resting.png diff --git a/doc/development/ux_guide/img/button-spam--active.png b/doc/development/ux_guide/img/button-spam--active.png Binary files differindex 55b44898684..a9e115f49c1 100644 --- a/doc/development/ux_guide/img/button-spam--active.png +++ b/doc/development/ux_guide/img/button-spam--active.png diff --git a/doc/development/ux_guide/img/button-spam--hover.png b/doc/development/ux_guide/img/button-spam--hover.png Binary files differindex 3dc8ed34c54..3b2c16430a6 100644 --- a/doc/development/ux_guide/img/button-spam--hover.png +++ b/doc/development/ux_guide/img/button-spam--hover.png diff --git a/doc/development/ux_guide/img/button-spam--resting.png b/doc/development/ux_guide/img/button-spam--resting.png Binary files differindex b6bf10a5b64..4f9f18ca68a 100644 --- a/doc/development/ux_guide/img/button-spam--resting.png +++ b/doc/development/ux_guide/img/button-spam--resting.png diff --git a/doc/development/ux_guide/img/button-success--active.png b/doc/development/ux_guide/img/button-success--active.png Binary files differindex 895a52831cb..b99f6f5e70e 100644 --- a/doc/development/ux_guide/img/button-success--active.png +++ b/doc/development/ux_guide/img/button-success--active.png diff --git a/doc/development/ux_guide/img/button-success--hover.png b/doc/development/ux_guide/img/button-success--hover.png Binary files differindex e4c74bd9778..0d0a61c679a 100644 --- a/doc/development/ux_guide/img/button-success--hover.png +++ b/doc/development/ux_guide/img/button-success--hover.png diff --git a/doc/development/ux_guide/img/button-success--resting.png b/doc/development/ux_guide/img/button-success--resting.png Binary files differindex 2fa971b5347..53b955c650a 100644 --- a/doc/development/ux_guide/img/button-success--resting.png +++ b/doc/development/ux_guide/img/button-success--resting.png diff --git a/doc/development/ux_guide/img/button-success-secondary--active.png b/doc/development/ux_guide/img/button-success-secondary--active.png Binary files differindex e7383b36946..333a91f2217 100644 --- a/doc/development/ux_guide/img/button-success-secondary--active.png +++ b/doc/development/ux_guide/img/button-success-secondary--active.png diff --git a/doc/development/ux_guide/img/button-success-secondary--hover.png b/doc/development/ux_guide/img/button-success-secondary--hover.png Binary files differindex 4af2a68cf1b..0cce59212e3 100644 --- a/doc/development/ux_guide/img/button-success-secondary--hover.png +++ b/doc/development/ux_guide/img/button-success-secondary--hover.png diff --git a/doc/development/ux_guide/img/button-success-secondary--resting.png b/doc/development/ux_guide/img/button-success-secondary--resting.png Binary files differindex a5a4ec512c8..2779a4949f8 100644 --- a/doc/development/ux_guide/img/button-success-secondary--resting.png +++ b/doc/development/ux_guide/img/button-success-secondary--resting.png diff --git a/doc/development/ux_guide/img/button-warning--active.png b/doc/development/ux_guide/img/button-warning--active.png Binary files differindex 5877d46c94d..f5760cd7c12 100644 --- a/doc/development/ux_guide/img/button-warning--active.png +++ b/doc/development/ux_guide/img/button-warning--active.png diff --git a/doc/development/ux_guide/img/button-warning--hover.png b/doc/development/ux_guide/img/button-warning--hover.png Binary files differindex 308e1adc8a3..a1f4c5cbcc6 100644 --- a/doc/development/ux_guide/img/button-warning--hover.png +++ b/doc/development/ux_guide/img/button-warning--hover.png diff --git a/doc/development/ux_guide/img/button-warning--resting.png b/doc/development/ux_guide/img/button-warning--resting.png Binary files differindex 28e5e601520..3d62fed5930 100644 --- a/doc/development/ux_guide/img/button-warning--resting.png +++ b/doc/development/ux_guide/img/button-warning--resting.png diff --git a/doc/development/ux_guide/img/color-blue.png b/doc/development/ux_guide/img/color-blue.png Binary files differindex 844e926f1f5..77c1a2cab31 100644 --- a/doc/development/ux_guide/img/color-blue.png +++ b/doc/development/ux_guide/img/color-blue.png diff --git a/doc/development/ux_guide/img/color-green.png b/doc/development/ux_guide/img/color-green.png Binary files differindex 5c4c23c7067..51600584c96 100644 --- a/doc/development/ux_guide/img/color-green.png +++ b/doc/development/ux_guide/img/color-green.png diff --git a/doc/development/ux_guide/img/color-grey.png b/doc/development/ux_guide/img/color-grey.png Binary files differindex 5247649a0ce..f0f0b9d80bb 100644 --- a/doc/development/ux_guide/img/color-grey.png +++ b/doc/development/ux_guide/img/color-grey.png diff --git a/doc/development/ux_guide/img/color-orange.png b/doc/development/ux_guide/img/color-orange.png Binary files differindex 1103c715225..f16435c0a64 100644 --- a/doc/development/ux_guide/img/color-orange.png +++ b/doc/development/ux_guide/img/color-orange.png diff --git a/doc/development/ux_guide/img/color-red.png b/doc/development/ux_guide/img/color-red.png Binary files differindex 77ecbbc0a20..5008e75da78 100644 --- a/doc/development/ux_guide/img/color-red.png +++ b/doc/development/ux_guide/img/color-red.png diff --git a/doc/development/ux_guide/img/components-anchorlinks.png b/doc/development/ux_guide/img/components-anchorlinks.png Binary files differindex 4a9c730566c..bd8d30f5905 100644 --- a/doc/development/ux_guide/img/components-anchorlinks.png +++ b/doc/development/ux_guide/img/components-anchorlinks.png diff --git a/doc/development/ux_guide/img/components-coverblock.png b/doc/development/ux_guide/img/components-coverblock.png Binary files differindex fb135f9648a..61160de5613 100644 --- a/doc/development/ux_guide/img/components-coverblock.png +++ b/doc/development/ux_guide/img/components-coverblock.png diff --git a/doc/development/ux_guide/img/components-dateexact.png b/doc/development/ux_guide/img/components-dateexact.png Binary files differindex 686ca727293..cc1fb8216bf 100644 --- a/doc/development/ux_guide/img/components-dateexact.png +++ b/doc/development/ux_guide/img/components-dateexact.png diff --git a/doc/development/ux_guide/img/components-fileholder.png b/doc/development/ux_guide/img/components-fileholder.png Binary files differindex ec2911a1232..5bf8565346a 100644 --- a/doc/development/ux_guide/img/components-fileholder.png +++ b/doc/development/ux_guide/img/components-fileholder.png diff --git a/doc/development/ux_guide/img/components-horizontalform.png b/doc/development/ux_guide/img/components-horizontalform.png Binary files differindex c57dceda43a..e6cbc69d20a 100644 --- a/doc/development/ux_guide/img/components-horizontalform.png +++ b/doc/development/ux_guide/img/components-horizontalform.png diff --git a/doc/development/ux_guide/img/components-listinsidepanel.png b/doc/development/ux_guide/img/components-listinsidepanel.png Binary files differindex 3a72d39bb5d..6b773a19954 100644 --- a/doc/development/ux_guide/img/components-listinsidepanel.png +++ b/doc/development/ux_guide/img/components-listinsidepanel.png diff --git a/doc/development/ux_guide/img/components-listwithhover.png b/doc/development/ux_guide/img/components-listwithhover.png Binary files differindex 8521a8ad53e..0826848ff34 100644 --- a/doc/development/ux_guide/img/components-listwithhover.png +++ b/doc/development/ux_guide/img/components-listwithhover.png diff --git a/doc/development/ux_guide/img/components-referencehover.png b/doc/development/ux_guide/img/components-referencehover.png Binary files differindex f80564dbb16..af5405d3e0b 100644 --- a/doc/development/ux_guide/img/components-referencehover.png +++ b/doc/development/ux_guide/img/components-referencehover.png diff --git a/doc/development/ux_guide/img/components-referenceissues.png b/doc/development/ux_guide/img/components-referenceissues.png Binary files differindex 51fb2cf3e43..4e175dc169d 100644 --- a/doc/development/ux_guide/img/components-referenceissues.png +++ b/doc/development/ux_guide/img/components-referenceissues.png diff --git a/doc/development/ux_guide/img/components-referencelabels.png b/doc/development/ux_guide/img/components-referencelabels.png Binary files differindex aba450cc3ba..29a985bbaa0 100644 --- a/doc/development/ux_guide/img/components-referencelabels.png +++ b/doc/development/ux_guide/img/components-referencelabels.png diff --git a/doc/development/ux_guide/img/components-referencemilestone.png b/doc/development/ux_guide/img/components-referencemilestone.png Binary files differindex adf2555ccf8..47c76a9d60f 100644 --- a/doc/development/ux_guide/img/components-referencemilestone.png +++ b/doc/development/ux_guide/img/components-referencemilestone.png diff --git a/doc/development/ux_guide/img/components-referencemrs.png b/doc/development/ux_guide/img/components-referencemrs.png Binary files differindex 6c3375f1ea1..9a5032a1516 100644 --- a/doc/development/ux_guide/img/components-referencemrs.png +++ b/doc/development/ux_guide/img/components-referencemrs.png diff --git a/doc/development/ux_guide/img/components-referencepeople.png b/doc/development/ux_guide/img/components-referencepeople.png Binary files differindex b8dd431e2e6..f9ef11be853 100644 --- a/doc/development/ux_guide/img/components-referencepeople.png +++ b/doc/development/ux_guide/img/components-referencepeople.png diff --git a/doc/development/ux_guide/img/components-searchbox.png b/doc/development/ux_guide/img/components-searchbox.png Binary files differindex a25189296ba..5c19024bfb0 100644 --- a/doc/development/ux_guide/img/components-searchbox.png +++ b/doc/development/ux_guide/img/components-searchbox.png diff --git a/doc/development/ux_guide/img/components-searchboxscoped.png b/doc/development/ux_guide/img/components-searchboxscoped.png Binary files differindex b116d714848..d4a35977658 100644 --- a/doc/development/ux_guide/img/components-searchboxscoped.png +++ b/doc/development/ux_guide/img/components-searchboxscoped.png diff --git a/doc/development/ux_guide/img/components-simplelist.png b/doc/development/ux_guide/img/components-simplelist.png Binary files differindex 858e5064c25..8d11c674e84 100644 --- a/doc/development/ux_guide/img/components-simplelist.png +++ b/doc/development/ux_guide/img/components-simplelist.png diff --git a/doc/development/ux_guide/img/features-contextualnav.png b/doc/development/ux_guide/img/features-contextualnav.png Binary files differindex f8466f28627..aa816776fad 100644 --- a/doc/development/ux_guide/img/features-contextualnav.png +++ b/doc/development/ux_guide/img/features-contextualnav.png diff --git a/doc/development/ux_guide/img/features-emptystates.png b/doc/development/ux_guide/img/features-emptystates.png Binary files differindex 51835a7080b..50f31f5e523 100644 --- a/doc/development/ux_guide/img/features-emptystates.png +++ b/doc/development/ux_guide/img/features-emptystates.png diff --git a/doc/development/ux_guide/img/icon-add.png b/doc/development/ux_guide/img/icon-add.png Binary files differindex bcad5e84591..f66525cc1b4 100644 --- a/doc/development/ux_guide/img/icon-add.png +++ b/doc/development/ux_guide/img/icon-add.png diff --git a/doc/development/ux_guide/img/icon-close.png b/doc/development/ux_guide/img/icon-close.png Binary files differindex dfe1495f5fa..af6c30ebe6a 100644 --- a/doc/development/ux_guide/img/icon-close.png +++ b/doc/development/ux_guide/img/icon-close.png diff --git a/doc/development/ux_guide/img/icon-edit.png b/doc/development/ux_guide/img/icon-edit.png Binary files differindex 50f6f841868..b9649f4aeec 100644 --- a/doc/development/ux_guide/img/icon-edit.png +++ b/doc/development/ux_guide/img/icon-edit.png diff --git a/doc/development/ux_guide/img/icon-notification.png b/doc/development/ux_guide/img/icon-notification.png Binary files differindex 6ddfaa44f66..5cf8f8ab59a 100644 --- a/doc/development/ux_guide/img/icon-notification.png +++ b/doc/development/ux_guide/img/icon-notification.png diff --git a/doc/development/ux_guide/img/icon-rss.png b/doc/development/ux_guide/img/icon-rss.png Binary files differindex b766488b32d..7e2987a2656 100644 --- a/doc/development/ux_guide/img/icon-rss.png +++ b/doc/development/ux_guide/img/icon-rss.png diff --git a/doc/development/ux_guide/img/icon-spec.png b/doc/development/ux_guide/img/icon-spec.png Binary files differindex 56b19610dc1..5bb85c5be98 100644 --- a/doc/development/ux_guide/img/icon-spec.png +++ b/doc/development/ux_guide/img/icon-spec.png diff --git a/doc/development/ux_guide/img/icon-subscribe.png b/doc/development/ux_guide/img/icon-subscribe.png Binary files differindex 650168296c6..7e2f5e6a1c6 100644 --- a/doc/development/ux_guide/img/icon-subscribe.png +++ b/doc/development/ux_guide/img/icon-subscribe.png diff --git a/doc/development/ux_guide/img/icon-trash.png b/doc/development/ux_guide/img/icon-trash.png Binary files differindex b02178ca992..bc46638fb2e 100644 --- a/doc/development/ux_guide/img/icon-trash.png +++ b/doc/development/ux_guide/img/icon-trash.png diff --git a/doc/development/ux_guide/img/illustrations-caps-do.png b/doc/development/ux_guide/img/illustrations-caps-do.png Binary files differindex 7a2c74382f6..f1030769b94 100644 --- a/doc/development/ux_guide/img/illustrations-caps-do.png +++ b/doc/development/ux_guide/img/illustrations-caps-do.png diff --git a/doc/development/ux_guide/img/illustrations-caps-don't.png b/doc/development/ux_guide/img/illustrations-caps-don't.png Binary files differindex 848f72dbe30..ab7abcaaf6f 100644 --- a/doc/development/ux_guide/img/illustrations-caps-don't.png +++ b/doc/development/ux_guide/img/illustrations-caps-don't.png diff --git a/doc/development/ux_guide/img/james-mackey.png b/doc/development/ux_guide/img/james-mackey.png Binary files differindex c8f9097f69f..f51a45c437b 100644 --- a/doc/development/ux_guide/img/james-mackey.png +++ b/doc/development/ux_guide/img/james-mackey.png diff --git a/doc/development/ux_guide/img/karolina-plaskaty.png b/doc/development/ux_guide/img/karolina-plaskaty.png Binary files differindex ae2e98b7bad..d1c9528dd5a 100644 --- a/doc/development/ux_guide/img/karolina-plaskaty.png +++ b/doc/development/ux_guide/img/karolina-plaskaty.png diff --git a/doc/development/ux_guide/img/matthieu-poirier.png b/doc/development/ux_guide/img/matthieu-poirier.png Binary files differindex dd21948ebe2..0ecc2d670d6 100644 --- a/doc/development/ux_guide/img/matthieu-poirier.png +++ b/doc/development/ux_guide/img/matthieu-poirier.png diff --git a/doc/development/ux_guide/img/modals-general-confimation-dialog.png b/doc/development/ux_guide/img/modals-general-confimation-dialog.png Binary files differindex 00a17374a0b..4ea0ea10ca7 100644 --- a/doc/development/ux_guide/img/modals-general-confimation-dialog.png +++ b/doc/development/ux_guide/img/modals-general-confimation-dialog.png diff --git a/doc/development/ux_guide/img/modals-layout-for-modals.png b/doc/development/ux_guide/img/modals-layout-for-modals.png Binary files differindex 6c7bc09e750..c481edd8250 100644 --- a/doc/development/ux_guide/img/modals-layout-for-modals.png +++ b/doc/development/ux_guide/img/modals-layout-for-modals.png diff --git a/doc/development/ux_guide/img/modals-special-confimation-dialog.png b/doc/development/ux_guide/img/modals-special-confimation-dialog.png Binary files differindex bf1e56326c5..d966010158b 100644 --- a/doc/development/ux_guide/img/modals-special-confimation-dialog.png +++ b/doc/development/ux_guide/img/modals-special-confimation-dialog.png diff --git a/doc/development/ux_guide/img/modals-three-buttons.png b/doc/development/ux_guide/img/modals-three-buttons.png Binary files differindex 519439e64e4..157d1b650bf 100644 --- a/doc/development/ux_guide/img/modals-three-buttons.png +++ b/doc/development/ux_guide/img/modals-three-buttons.png diff --git a/doc/development/ux_guide/img/nazim-ramesh.png b/doc/development/ux_guide/img/nazim-ramesh.png Binary files differindex cc3e197679d..dad2b37010b 100644 --- a/doc/development/ux_guide/img/nazim-ramesh.png +++ b/doc/development/ux_guide/img/nazim-ramesh.png diff --git a/doc/development/ux_guide/img/popover-placement-above.png b/doc/development/ux_guide/img/popover-placement-above.png Binary files differindex 1aa044bfc9c..84c9c878ec2 100644 --- a/doc/development/ux_guide/img/popover-placement-above.png +++ b/doc/development/ux_guide/img/popover-placement-above.png diff --git a/doc/development/ux_guide/img/popover-placement-below.png b/doc/development/ux_guide/img/popover-placement-below.png Binary files differindex 2d6ab8a1618..f6f18199ab6 100644 --- a/doc/development/ux_guide/img/popover-placement-below.png +++ b/doc/development/ux_guide/img/popover-placement-below.png diff --git a/doc/development/ux_guide/img/surfaces-contentitemtitle.png b/doc/development/ux_guide/img/surfaces-contentitemtitle.png Binary files differindex 3af0b56c8fb..f6cd212ecfd 100644 --- a/doc/development/ux_guide/img/surfaces-contentitemtitle.png +++ b/doc/development/ux_guide/img/surfaces-contentitemtitle.png diff --git a/doc/development/ux_guide/img/surfaces-systeminformationblock.png b/doc/development/ux_guide/img/surfaces-systeminformationblock.png Binary files differindex 9f42f1d4dd0..f3313add2b8 100644 --- a/doc/development/ux_guide/img/surfaces-systeminformationblock.png +++ b/doc/development/ux_guide/img/surfaces-systeminformationblock.png diff --git a/doc/development/ux_guide/img/surfaces-ux.png b/doc/development/ux_guide/img/surfaces-ux.png Binary files differindex 53208727c64..eaa7f70c0c7 100644 --- a/doc/development/ux_guide/img/surfaces-ux.png +++ b/doc/development/ux_guide/img/surfaces-ux.png diff --git a/doc/development/ux_guide/img/tooltip-placement.png b/doc/development/ux_guide/img/tooltip-placement.png Binary files differindex 061f82e4df0..da49c192878 100644 --- a/doc/development/ux_guide/img/tooltip-placement.png +++ b/doc/development/ux_guide/img/tooltip-placement.png diff --git a/doc/development/ux_guide/img/tooltip-usage.png b/doc/development/ux_guide/img/tooltip-usage.png Binary files differindex 40c4f051cd0..4f5884c4b48 100644 --- a/doc/development/ux_guide/img/tooltip-usage.png +++ b/doc/development/ux_guide/img/tooltip-usage.png diff --git a/doc/gitlab-basics/img/create_new_project_info.png b/doc/gitlab-basics/img/create_new_project_info.png Binary files differindex b4119dc046a..2693a7f9a6d 100644 --- a/doc/gitlab-basics/img/create_new_project_info.png +++ b/doc/gitlab-basics/img/create_new_project_info.png diff --git a/doc/gitlab-basics/img/fork_new.png b/doc/gitlab-basics/img/fork_new.png Binary files differindex fa185fdaca1..7bbc3d8fbae 100644 --- a/doc/gitlab-basics/img/fork_new.png +++ b/doc/gitlab-basics/img/fork_new.png diff --git a/doc/gitlab-basics/img/merge_request_select_branch.png b/doc/gitlab-basics/img/merge_request_select_branch.png Binary files differindex 57ea0e65f34..b1dec975f9b 100644 --- a/doc/gitlab-basics/img/merge_request_select_branch.png +++ b/doc/gitlab-basics/img/merge_request_select_branch.png diff --git a/doc/gitlab-basics/img/profile_settings.png b/doc/gitlab-basics/img/profile_settings.png Binary files differindex aaa1a39313d..b91b698fb18 100644 --- a/doc/gitlab-basics/img/profile_settings.png +++ b/doc/gitlab-basics/img/profile_settings.png diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png b/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png Binary files differindex 5e501ec86ef..0b1c64a72f3 100644 --- a/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png +++ b/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png b/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png Binary files differindex 7ebb8973ef0..8014f1d5301 100644 --- a/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png +++ b/doc/gitlab-basics/img/profile_settings_ssh_keys_single_key.png diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png b/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png Binary files differindex 89a04c17fed..02ca0bf7478 100644 --- a/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png +++ b/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png diff --git a/doc/img/devops_lifecycle.png b/doc/img/devops_lifecycle.png Binary files differindex 0616be46df8..0b15e9619a5 100644 --- a/doc/img/devops_lifecycle.png +++ b/doc/img/devops_lifecycle.png diff --git a/doc/install/google_cloud_platform/img/boot_disk.png b/doc/install/google_cloud_platform/img/boot_disk.png Binary files differindex 37b2d9eaae7..b9f7eed6601 100644 --- a/doc/install/google_cloud_platform/img/boot_disk.png +++ b/doc/install/google_cloud_platform/img/boot_disk.png diff --git a/doc/install/google_cloud_platform/img/first_signin.png b/doc/install/google_cloud_platform/img/first_signin.png Binary files differindex 6eb3392d674..1e218abf63d 100644 --- a/doc/install/google_cloud_platform/img/first_signin.png +++ b/doc/install/google_cloud_platform/img/first_signin.png diff --git a/doc/install/google_cloud_platform/img/gcp_landing.png b/doc/install/google_cloud_platform/img/gcp_landing.png Binary files differindex d6390c4dd4f..92a9873728c 100644 --- a/doc/install/google_cloud_platform/img/gcp_landing.png +++ b/doc/install/google_cloud_platform/img/gcp_landing.png diff --git a/doc/install/google_cloud_platform/img/launch_vm.png b/doc/install/google_cloud_platform/img/launch_vm.png Binary files differindex 3fd13f232bb..53cb23277fd 100644 --- a/doc/install/google_cloud_platform/img/launch_vm.png +++ b/doc/install/google_cloud_platform/img/launch_vm.png diff --git a/doc/install/google_cloud_platform/img/ssh_terminal.png b/doc/install/google_cloud_platform/img/ssh_terminal.png Binary files differindex 6a1a418d8e9..171cb572074 100644 --- a/doc/install/google_cloud_platform/img/ssh_terminal.png +++ b/doc/install/google_cloud_platform/img/ssh_terminal.png diff --git a/doc/install/google_cloud_platform/img/vm_created.png b/doc/install/google_cloud_platform/img/vm_created.png Binary files differindex fb467f40838..0ba422af60c 100644 --- a/doc/install/google_cloud_platform/img/vm_created.png +++ b/doc/install/google_cloud_platform/img/vm_created.png diff --git a/doc/install/google_cloud_platform/img/vm_details.png b/doc/install/google_cloud_platform/img/vm_details.png Binary files differindex 2d230416a4b..85b9ca066c8 100644 --- a/doc/install/google_cloud_platform/img/vm_details.png +++ b/doc/install/google_cloud_platform/img/vm_details.png diff --git a/doc/install/installation.md b/doc/install/installation.md index a310f12b29e..2d657163721 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -457,11 +457,35 @@ GitLab-Pages uses [GNU Make](https://www.gnu.org/software/make/). This step is o sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION) sudo -u git -H make +### Install Gitaly + + # Fetch Gitaly source with Git and compile with Go + sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly]" RAILS_ENV=production + +You can specify a different Git repository by providing it as an extra parameter: + + sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,https://example.com/gitaly.git]" RAILS_ENV=production + +Next, make sure gitaly configured: + + # Restrict Gitaly socket access + sudo chmod 0700 /home/git/gitlab/tmp/sockets/private + sudo chown git /home/git/gitlab/tmp/sockets/private + + # If you are using non-default settings you need to update config.toml + cd /home/git/gitaly + sudo -u git -H editor config.toml + +For more information about configuring Gitaly see +[doc/administration/gitaly](../administration/gitaly). + ### Initialize Database and Activate Advanced Features sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production - # Type 'yes' to create the database tables. + + # or you can skip the question by adding force=yes + sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production force=yes # When done you see 'Administrator account created:' @@ -491,28 +515,6 @@ Make GitLab start on boot: sudo update-rc.d gitlab defaults 21 -### Install Gitaly - - # Fetch Gitaly source with Git and compile with Go - sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,/home/git/repositories]" RAILS_ENV=production - -You can specify a different Git repository by providing it as an extra parameter: - - sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,/home/git/repositories,https://example.com/gitaly.git]" RAILS_ENV=production - -Next, make sure gitaly configured: - - # Restrict Gitaly socket access - sudo chmod 0700 /home/git/gitlab/tmp/sockets/private - sudo chown git /home/git/gitlab/tmp/sockets/private - - # If you are using non-default settings you need to update config.toml - cd /home/git/gitaly - sudo -u git -H editor config.toml - -For more information about configuring Gitaly see -[doc/administration/gitaly](../administration/gitaly). - ### Setup Logrotate sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab diff --git a/doc/install/kubernetes/gitlab_chart.md b/doc/install/kubernetes/gitlab_chart.md index 4e636ae3399..5a6f26319c7 100644 --- a/doc/install/kubernetes/gitlab_chart.md +++ b/doc/install/kubernetes/gitlab_chart.md @@ -30,7 +30,7 @@ The `gitlab` chart includes all required dependencies, and takes a few minutes to deploy. TIP: **Tip:** -For large scale deployments, we strongly recommend using the +For production deployments, we strongly recommend using the [detailed installation instructions](https://gitlab.com/charts/gitlab/blob/master/doc/installation/README.md) utilizing [external Postgres, Redis, and object storage](https://gitlab.com/charts/gitlab/tree/master/doc/advanced) services. diff --git a/doc/install/openshift_and_gitlab/img/add-gitlab-to-project.png b/doc/install/openshift_and_gitlab/img/add-gitlab-to-project.png Binary files differindex fcad4e59ae3..5b6059dd022 100644 --- a/doc/install/openshift_and_gitlab/img/add-gitlab-to-project.png +++ b/doc/install/openshift_and_gitlab/img/add-gitlab-to-project.png diff --git a/doc/install/openshift_and_gitlab/img/add-to-project.png b/doc/install/openshift_and_gitlab/img/add-to-project.png Binary files differindex bd915a229f6..f9b00431d00 100644 --- a/doc/install/openshift_and_gitlab/img/add-to-project.png +++ b/doc/install/openshift_and_gitlab/img/add-to-project.png diff --git a/doc/install/openshift_and_gitlab/img/create-project-ui.png b/doc/install/openshift_and_gitlab/img/create-project-ui.png Binary files differindex e72866f252a..43b151264c5 100644 --- a/doc/install/openshift_and_gitlab/img/create-project-ui.png +++ b/doc/install/openshift_and_gitlab/img/create-project-ui.png diff --git a/doc/install/openshift_and_gitlab/img/gitlab-logs.png b/doc/install/openshift_and_gitlab/img/gitlab-logs.png Binary files differindex 1e24080c7df..8b90b2f74ac 100644 --- a/doc/install/openshift_and_gitlab/img/gitlab-logs.png +++ b/doc/install/openshift_and_gitlab/img/gitlab-logs.png diff --git a/doc/install/openshift_and_gitlab/img/gitlab-overview.png b/doc/install/openshift_and_gitlab/img/gitlab-overview.png Binary files differindex 3c5df0ea101..3a7bec7c2bc 100644 --- a/doc/install/openshift_and_gitlab/img/gitlab-overview.png +++ b/doc/install/openshift_and_gitlab/img/gitlab-overview.png diff --git a/doc/install/openshift_and_gitlab/img/gitlab-running.png b/doc/install/openshift_and_gitlab/img/gitlab-running.png Binary files differindex c7db691cb30..0fcd9f00d08 100644 --- a/doc/install/openshift_and_gitlab/img/gitlab-running.png +++ b/doc/install/openshift_and_gitlab/img/gitlab-running.png diff --git a/doc/install/openshift_and_gitlab/img/gitlab-scale.png b/doc/install/openshift_and_gitlab/img/gitlab-scale.png Binary files differindex 4903c7d7498..ebae8b588b1 100644 --- a/doc/install/openshift_and_gitlab/img/gitlab-scale.png +++ b/doc/install/openshift_and_gitlab/img/gitlab-scale.png diff --git a/doc/install/openshift_and_gitlab/img/gitlab-settings.png b/doc/install/openshift_and_gitlab/img/gitlab-settings.png Binary files differindex db4360ffef0..0dd1e1f5b8e 100644 --- a/doc/install/openshift_and_gitlab/img/gitlab-settings.png +++ b/doc/install/openshift_and_gitlab/img/gitlab-settings.png diff --git a/doc/install/openshift_and_gitlab/img/no-resources.png b/doc/install/openshift_and_gitlab/img/no-resources.png Binary files differindex 480fb766468..1ef0a0b31e5 100644 --- a/doc/install/openshift_and_gitlab/img/no-resources.png +++ b/doc/install/openshift_and_gitlab/img/no-resources.png diff --git a/doc/install/openshift_and_gitlab/img/openshift-infra-project.png b/doc/install/openshift_and_gitlab/img/openshift-infra-project.png Binary files differindex 8b9f85aa341..e31dda1461c 100644 --- a/doc/install/openshift_and_gitlab/img/openshift-infra-project.png +++ b/doc/install/openshift_and_gitlab/img/openshift-infra-project.png diff --git a/doc/install/openshift_and_gitlab/img/pods-overview.png b/doc/install/openshift_and_gitlab/img/pods-overview.png Binary files differindex e1cf08bd217..65927f65f4f 100644 --- a/doc/install/openshift_and_gitlab/img/pods-overview.png +++ b/doc/install/openshift_and_gitlab/img/pods-overview.png diff --git a/doc/install/openshift_and_gitlab/img/rc-name.png b/doc/install/openshift_and_gitlab/img/rc-name.png Binary files differindex 889e34adbec..16d967b8460 100644 --- a/doc/install/openshift_and_gitlab/img/rc-name.png +++ b/doc/install/openshift_and_gitlab/img/rc-name.png diff --git a/doc/install/openshift_and_gitlab/img/running-pods.png b/doc/install/openshift_and_gitlab/img/running-pods.png Binary files differindex 3fd4e56662f..e08487c881c 100644 --- a/doc/install/openshift_and_gitlab/img/running-pods.png +++ b/doc/install/openshift_and_gitlab/img/running-pods.png diff --git a/doc/install/openshift_and_gitlab/img/storage-volumes.png b/doc/install/openshift_and_gitlab/img/storage-volumes.png Binary files differindex ae1e5381faa..3fd092919bb 100644 --- a/doc/install/openshift_and_gitlab/img/storage-volumes.png +++ b/doc/install/openshift_and_gitlab/img/storage-volumes.png diff --git a/doc/install/openshift_and_gitlab/img/web-console.png b/doc/install/openshift_and_gitlab/img/web-console.png Binary files differindex aa1425d4f94..012d7703c73 100644 --- a/doc/install/openshift_and_gitlab/img/web-console.png +++ b/doc/install/openshift_and_gitlab/img/web-console.png diff --git a/doc/integration/img/bitbucket_oauth_keys.png b/doc/integration/img/bitbucket_oauth_keys.png Binary files differindex 6dd2c7d744e..2f0c0eff784 100644 --- a/doc/integration/img/bitbucket_oauth_keys.png +++ b/doc/integration/img/bitbucket_oauth_keys.png diff --git a/doc/integration/img/enable_trello_powerup.png b/doc/integration/img/enable_trello_powerup.png Binary files differindex 65d01f1c38c..f80d0eadc0b 100644 --- a/doc/integration/img/enable_trello_powerup.png +++ b/doc/integration/img/enable_trello_powerup.png diff --git a/doc/integration/img/enabled-oauth-sign-in-sources.png b/doc/integration/img/enabled-oauth-sign-in-sources.png Binary files differindex f145aeae75c..e83f9d5cfdf 100644 --- a/doc/integration/img/enabled-oauth-sign-in-sources.png +++ b/doc/integration/img/enabled-oauth-sign-in-sources.png diff --git a/doc/integration/img/facebook_api_keys.png b/doc/integration/img/facebook_api_keys.png Binary files differindex 9463ec1e7a3..7480b144091 100644 --- a/doc/integration/img/facebook_api_keys.png +++ b/doc/integration/img/facebook_api_keys.png diff --git a/doc/integration/img/facebook_website_url.png b/doc/integration/img/facebook_website_url.png Binary files differindex 67d78d13951..7873c9905f1 100644 --- a/doc/integration/img/facebook_website_url.png +++ b/doc/integration/img/facebook_website_url.png diff --git a/doc/integration/img/gitlab_app.png b/doc/integration/img/gitlab_app.png Binary files differindex 8d6a4456fc4..228e8a01305 100644 --- a/doc/integration/img/gitlab_app.png +++ b/doc/integration/img/gitlab_app.png diff --git a/doc/integration/img/google_app.png b/doc/integration/img/google_app.png Binary files differindex 9fda06dabb1..08f230452b4 100644 --- a/doc/integration/img/google_app.png +++ b/doc/integration/img/google_app.png diff --git a/doc/integration/img/oauth_provider_admin_application.png b/doc/integration/img/oauth_provider_admin_application.png Binary files differindex c8ecce129c8..353114fea30 100644 --- a/doc/integration/img/oauth_provider_admin_application.png +++ b/doc/integration/img/oauth_provider_admin_application.png diff --git a/doc/integration/img/oauth_provider_application_form.png b/doc/integration/img/oauth_provider_application_form.png Binary files differindex 954681e054e..c4546d8b3f5 100644 --- a/doc/integration/img/oauth_provider_application_form.png +++ b/doc/integration/img/oauth_provider_application_form.png diff --git a/doc/integration/img/oauth_provider_application_id_secret.png b/doc/integration/img/oauth_provider_application_id_secret.png Binary files differindex 65cca5f1e1b..21e442b5d04 100644 --- a/doc/integration/img/oauth_provider_application_id_secret.png +++ b/doc/integration/img/oauth_provider_application_id_secret.png diff --git a/doc/integration/img/oauth_provider_authorized_application.png b/doc/integration/img/oauth_provider_authorized_application.png Binary files differindex ed99db3476d..ebff8529b4e 100644 --- a/doc/integration/img/oauth_provider_authorized_application.png +++ b/doc/integration/img/oauth_provider_authorized_application.png diff --git a/doc/integration/img/submit_issue.png b/doc/integration/img/submit_issue.png Binary files differindex 8accb78faf3..e794eac189e 100644 --- a/doc/integration/img/submit_issue.png +++ b/doc/integration/img/submit_issue.png diff --git a/doc/integration/img/twitter_app_api_keys.png b/doc/integration/img/twitter_app_api_keys.png Binary files differindex 34e3c3ba001..c6a3245b1b1 100644 --- a/doc/integration/img/twitter_app_api_keys.png +++ b/doc/integration/img/twitter_app_api_keys.png diff --git a/doc/monitoring/performance/img/grafana_dashboard_import.png b/doc/monitoring/performance/img/grafana_dashboard_import.png Binary files differindex 7761ea00522..fd639ee0eb8 100644 --- a/doc/monitoring/performance/img/grafana_dashboard_import.png +++ b/doc/monitoring/performance/img/grafana_dashboard_import.png diff --git a/doc/monitoring/performance/img/grafana_data_source_configuration.png b/doc/monitoring/performance/img/grafana_data_source_configuration.png Binary files differindex 3e749eb8f9d..a98e0ed1e7d 100644 --- a/doc/monitoring/performance/img/grafana_data_source_configuration.png +++ b/doc/monitoring/performance/img/grafana_data_source_configuration.png diff --git a/doc/monitoring/performance/img/grafana_data_source_empty.png b/doc/monitoring/performance/img/grafana_data_source_empty.png Binary files differindex 33fcaaaef64..549ada8343e 100644 --- a/doc/monitoring/performance/img/grafana_data_source_empty.png +++ b/doc/monitoring/performance/img/grafana_data_source_empty.png diff --git a/doc/monitoring/performance/img/grafana_save_icon.png b/doc/monitoring/performance/img/grafana_save_icon.png Binary files differindex c18f2147e9d..68a071f5ae2 100644 --- a/doc/monitoring/performance/img/grafana_save_icon.png +++ b/doc/monitoring/performance/img/grafana_save_icon.png diff --git a/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png b/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png Binary files differindex d96a18ebc04..b9563a00e97 100644 --- a/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png +++ b/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png diff --git a/doc/public_access/img/restrict_visibility_levels.png b/doc/public_access/img/restrict_visibility_levels.png Binary files differindex c7d4d87981f..e9315cfb701 100644 --- a/doc/public_access/img/restrict_visibility_levels.png +++ b/doc/public_access/img/restrict_visibility_levels.png diff --git a/doc/raketasks/backup_hrz.png b/doc/raketasks/backup_hrz.png Binary files differindex c9595b236ee..32690b2904c 100644 --- a/doc/raketasks/backup_hrz.png +++ b/doc/raketasks/backup_hrz.png diff --git a/doc/security/img/outbound_requests_section.png b/doc/security/img/outbound_requests_section.png Binary files differindex 95c9c6ee771..f7783f34cdd 100644 --- a/doc/security/img/outbound_requests_section.png +++ b/doc/security/img/outbound_requests_section.png diff --git a/doc/security/img/ssh_keys_restrictions_settings.png b/doc/security/img/ssh_keys_restrictions_settings.png Binary files differindex 2e918fd4b3f..94258af3bf9 100644 --- a/doc/security/img/ssh_keys_restrictions_settings.png +++ b/doc/security/img/ssh_keys_restrictions_settings.png diff --git a/doc/security/img/two_factor_authentication_group_settings.png b/doc/security/img/two_factor_authentication_group_settings.png Binary files differindex a1b3c58bfdc..05d95554fd9 100644 --- a/doc/security/img/two_factor_authentication_group_settings.png +++ b/doc/security/img/two_factor_authentication_group_settings.png diff --git a/doc/security/img/two_factor_authentication_settings.png b/doc/security/img/two_factor_authentication_settings.png Binary files differindex 6d89be1eb04..2a2208f98bd 100644 --- a/doc/security/img/two_factor_authentication_settings.png +++ b/doc/security/img/two_factor_authentication_settings.png diff --git a/doc/topics/autodevops/img/guide_environments.png b/doc/topics/autodevops/img/guide_environments.png Binary files differindex 1d8d5614e64..404db17c57a 100644 --- a/doc/topics/autodevops/img/guide_environments.png +++ b/doc/topics/autodevops/img/guide_environments.png diff --git a/doc/topics/autodevops/img/guide_ide_commit.png b/doc/topics/autodevops/img/guide_ide_commit.png Binary files differindex 188f60f2a4b..d7be66f4049 100644 --- a/doc/topics/autodevops/img/guide_ide_commit.png +++ b/doc/topics/autodevops/img/guide_ide_commit.png diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/img/branching.png b/doc/topics/git/numerous_undo_possibilities_in_git/img/branching.png Binary files differindex 9a80c211c99..d8dc9fc8097 100644 --- a/doc/topics/git/numerous_undo_possibilities_in_git/img/branching.png +++ b/doc/topics/git/numerous_undo_possibilities_in_git/img/branching.png diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/img/rebase_reset.png b/doc/topics/git/numerous_undo_possibilities_in_git/img/rebase_reset.png Binary files differindex ac7ea9ecddc..6506de209f4 100644 --- a/doc/topics/git/numerous_undo_possibilities_in_git/img/rebase_reset.png +++ b/doc/topics/git/numerous_undo_possibilities_in_git/img/rebase_reset.png diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/img/revert.png b/doc/topics/git/numerous_undo_possibilities_in_git/img/revert.png Binary files differindex 13b3a35ca45..040f8118d72 100644 --- a/doc/topics/git/numerous_undo_possibilities_in_git/img/revert.png +++ b/doc/topics/git/numerous_undo_possibilities_in_git/img/revert.png diff --git a/doc/university/high-availability/aws/img/auto-scaling-det.png b/doc/university/high-availability/aws/img/auto-scaling-det.png Binary files differindex 1e125f301bc..cf32c024bf8 100644 --- a/doc/university/high-availability/aws/img/auto-scaling-det.png +++ b/doc/university/high-availability/aws/img/auto-scaling-det.png diff --git a/doc/university/high-availability/aws/img/db-subnet-group.png b/doc/university/high-availability/aws/img/db-subnet-group.png Binary files differindex 590a02b8dbe..875184af310 100644 --- a/doc/university/high-availability/aws/img/db-subnet-group.png +++ b/doc/university/high-availability/aws/img/db-subnet-group.png diff --git a/doc/university/high-availability/aws/img/ig.png b/doc/university/high-availability/aws/img/ig.png Binary files differindex d4fc2d12de8..2798d4beac3 100644 --- a/doc/university/high-availability/aws/img/ig.png +++ b/doc/university/high-availability/aws/img/ig.png diff --git a/doc/university/high-availability/aws/img/instance_specs.png b/doc/university/high-availability/aws/img/instance_specs.png Binary files differindex 650f375ab3c..2a2b80103fb 100644 --- a/doc/university/high-availability/aws/img/instance_specs.png +++ b/doc/university/high-availability/aws/img/instance_specs.png diff --git a/doc/university/high-availability/aws/img/new_vpc.png b/doc/university/high-availability/aws/img/new_vpc.png Binary files differindex e51c066cee2..d872554fab7 100644 --- a/doc/university/high-availability/aws/img/new_vpc.png +++ b/doc/university/high-availability/aws/img/new_vpc.png diff --git a/doc/university/high-availability/aws/img/policies.png b/doc/university/high-availability/aws/img/policies.png Binary files differindex afcd9e4af9b..e99497a52a2 100644 --- a/doc/university/high-availability/aws/img/policies.png +++ b/doc/university/high-availability/aws/img/policies.png diff --git a/doc/university/high-availability/aws/img/rds-net-opt.png b/doc/university/high-availability/aws/img/rds-net-opt.png Binary files differindex 651cc23b1ab..13130ac96b8 100644 --- a/doc/university/high-availability/aws/img/rds-net-opt.png +++ b/doc/university/high-availability/aws/img/rds-net-opt.png diff --git a/doc/university/high-availability/aws/img/rds-sec-group.png b/doc/university/high-availability/aws/img/rds-sec-group.png Binary files differindex c6d1bc350e4..a88caba62c2 100644 --- a/doc/university/high-availability/aws/img/rds-sec-group.png +++ b/doc/university/high-availability/aws/img/rds-sec-group.png diff --git a/doc/university/high-availability/aws/img/subnet.png b/doc/university/high-availability/aws/img/subnet.png Binary files differindex de910edc948..681c29bf07a 100644 --- a/doc/university/high-availability/aws/img/subnet.png +++ b/doc/university/high-availability/aws/img/subnet.png diff --git a/doc/university/training/gitlab_flow/production_branch.png b/doc/university/training/gitlab_flow/production_branch.png Binary files differindex 66456cc51af..956761d7eb8 100644 --- a/doc/university/training/gitlab_flow/production_branch.png +++ b/doc/university/training/gitlab_flow/production_branch.png diff --git a/doc/university/training/gitlab_flow/release_branches.png b/doc/university/training/gitlab_flow/release_branches.png Binary files differindex 5661e36c4e2..dcb5f97dff0 100644 --- a/doc/university/training/gitlab_flow/release_branches.png +++ b/doc/university/training/gitlab_flow/release_branches.png diff --git a/doc/update/10.7-to-10.8.md b/doc/update/10.7-to-10.8.md index 13101a987f4..7bb628f9740 100644 --- a/doc/update/10.7-to-10.8.md +++ b/doc/update/10.7-to-10.8.md @@ -38,16 +38,16 @@ You can check which version you are running with `ruby -v`. Download Ruby and compile it: - ```bash - mkdir /tmp/ruby && cd /tmp/ruby - curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.7.tar.gz - echo '540996fec64984ab6099e34d2f5820b14904f15a ruby-2.3.7.tar.gz' | shasum -c - && tar xzf ruby-2.3.7.tar.gz - cd ruby-2.3.7 - - ./configure --disable-install-rdoc - make - sudo make install - ``` +```bash +mkdir /tmp/ruby && cd /tmp/ruby +curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.7.tar.gz +echo '540996fec64984ab6099e34d2f5820b14904f15a ruby-2.3.7.tar.gz' | shasum -c - && tar xzf ruby-2.3.7.tar.gz +cd ruby-2.3.7 + +./configure --disable-install-rdoc +make +sudo make install +``` Install Bundler: diff --git a/doc/user/admin_area/monitoring/img/health_check_token.png b/doc/user/admin_area/monitoring/img/health_check_token.png Binary files differindex 182549fc484..8d4cf710176 100644 --- a/doc/user/admin_area/monitoring/img/health_check_token.png +++ b/doc/user/admin_area/monitoring/img/health_check_token.png diff --git a/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png b/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png Binary files differindex 50a86ede56b..723be23e77b 100644 --- a/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png +++ b/doc/user/admin_area/settings/img/admin_area_default_artifacts_expiration.png diff --git a/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png b/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png Binary files differindex 33fd29e2039..3f827f1f7a3 100644 --- a/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png +++ b/doc/user/admin_area/settings/img/admin_area_maximum_artifacts_size.png diff --git a/doc/user/admin_area/settings/img/domain_blacklist.png b/doc/user/admin_area/settings/img/domain_blacklist.png Binary files differindex dedd3be1e8f..a7e972b7c0a 100644 --- a/doc/user/admin_area/settings/img/domain_blacklist.png +++ b/doc/user/admin_area/settings/img/domain_blacklist.png diff --git a/doc/user/admin_area/settings/img/restricted_url.png b/doc/user/admin_area/settings/img/restricted_url.png Binary files differindex 67abd13f741..c71abf0a226 100644 --- a/doc/user/admin_area/settings/img/restricted_url.png +++ b/doc/user/admin_area/settings/img/restricted_url.png diff --git a/doc/user/admin_area/settings/img/update-available.png b/doc/user/admin_area/settings/img/update-available.png Binary files differindex 0dafdad618e..9887e06c7dc 100644 --- a/doc/user/admin_area/settings/img/update-available.png +++ b/doc/user/admin_area/settings/img/update-available.png diff --git a/doc/user/discussions/img/automatically_resolve_outdated_discussions.png b/doc/user/discussions/img/automatically_resolve_outdated_discussions.png Binary files differindex 9a798ddd178..ba129e7a618 100644 --- a/doc/user/discussions/img/automatically_resolve_outdated_discussions.png +++ b/doc/user/discussions/img/automatically_resolve_outdated_discussions.png diff --git a/doc/user/discussions/img/btn_new_issue_for_all_discussions.png b/doc/user/discussions/img/btn_new_issue_for_all_discussions.png Binary files differindex b15447ec290..3306bf2e60e 100644 --- a/doc/user/discussions/img/btn_new_issue_for_all_discussions.png +++ b/doc/user/discussions/img/btn_new_issue_for_all_discussions.png diff --git a/doc/user/discussions/img/discussion_comment.png b/doc/user/discussions/img/discussion_comment.png Binary files differindex 8f66d138922..206ddebf54b 100644 --- a/doc/user/discussions/img/discussion_comment.png +++ b/doc/user/discussions/img/discussion_comment.png diff --git a/doc/user/discussions/img/discussion_lock_system_notes.png b/doc/user/discussions/img/discussion_lock_system_notes.png Binary files differindex 8e8e8e0bc3d..44a47e3f097 100644 --- a/doc/user/discussions/img/discussion_lock_system_notes.png +++ b/doc/user/discussions/img/discussion_lock_system_notes.png diff --git a/doc/user/discussions/img/discussion_view.png b/doc/user/discussions/img/discussion_view.png Binary files differindex 2ee1db2eab3..3a2b766ed7e 100644 --- a/doc/user/discussions/img/discussion_view.png +++ b/doc/user/discussions/img/discussion_view.png diff --git a/doc/user/discussions/img/lock_form_member.png b/doc/user/discussions/img/lock_form_member.png Binary files differindex 01c6308d24c..7bfcb4faae6 100644 --- a/doc/user/discussions/img/lock_form_member.png +++ b/doc/user/discussions/img/lock_form_member.png diff --git a/doc/user/discussions/img/lock_form_non_member.png b/doc/user/discussions/img/lock_form_non_member.png Binary files differindex 3bb70b69580..59e5fd89499 100644 --- a/doc/user/discussions/img/lock_form_non_member.png +++ b/doc/user/discussions/img/lock_form_non_member.png diff --git a/doc/user/discussions/img/new_issue_for_discussion.png b/doc/user/discussions/img/new_issue_for_discussion.png Binary files differindex 93c9dad8921..819d872a9a2 100644 --- a/doc/user/discussions/img/new_issue_for_discussion.png +++ b/doc/user/discussions/img/new_issue_for_discussion.png diff --git a/doc/user/discussions/img/only_allow_merge_if_all_discussions_are_resolved_msg.png b/doc/user/discussions/img/only_allow_merge_if_all_discussions_are_resolved_msg.png Binary files differindex bcdc0250d7c..9044926b0eb 100644 --- a/doc/user/discussions/img/only_allow_merge_if_all_discussions_are_resolved_msg.png +++ b/doc/user/discussions/img/only_allow_merge_if_all_discussions_are_resolved_msg.png diff --git a/doc/user/discussions/img/preview_issue_for_discussion.png b/doc/user/discussions/img/preview_issue_for_discussion.png Binary files differindex 2ee0653b2ba..30c273ca4c5 100644 --- a/doc/user/discussions/img/preview_issue_for_discussion.png +++ b/doc/user/discussions/img/preview_issue_for_discussion.png diff --git a/doc/user/discussions/img/preview_issue_for_discussions.png b/doc/user/discussions/img/preview_issue_for_discussions.png Binary files differindex 3fe0a666678..3d906e1b0b0 100644 --- a/doc/user/discussions/img/preview_issue_for_discussions.png +++ b/doc/user/discussions/img/preview_issue_for_discussions.png diff --git a/doc/user/discussions/img/resolve_comment_button.png b/doc/user/discussions/img/resolve_comment_button.png Binary files differindex 70340108874..7c19fac31a2 100644 --- a/doc/user/discussions/img/resolve_comment_button.png +++ b/doc/user/discussions/img/resolve_comment_button.png diff --git a/doc/user/discussions/img/resolve_discussion_issue_notice.png b/doc/user/discussions/img/resolve_discussion_issue_notice.png Binary files differindex e0ee6a39ffd..ed50dc1de91 100644 --- a/doc/user/discussions/img/resolve_discussion_issue_notice.png +++ b/doc/user/discussions/img/resolve_discussion_issue_notice.png diff --git a/doc/user/discussions/img/resolve_discussion_open_issue.png b/doc/user/discussions/img/resolve_discussion_open_issue.png Binary files differindex 98d63278326..9d0a14671d6 100644 --- a/doc/user/discussions/img/resolve_discussion_open_issue.png +++ b/doc/user/discussions/img/resolve_discussion_open_issue.png diff --git a/doc/user/discussions/img/turn_off_lock.png b/doc/user/discussions/img/turn_off_lock.png Binary files differindex dd05b398a8b..aae1def6f72 100644 --- a/doc/user/discussions/img/turn_off_lock.png +++ b/doc/user/discussions/img/turn_off_lock.png diff --git a/doc/user/discussions/img/turn_on_lock.png b/doc/user/discussions/img/turn_on_lock.png Binary files differindex 9597da4e14d..f36ffc8831b 100644 --- a/doc/user/discussions/img/turn_on_lock.png +++ b/doc/user/discussions/img/turn_on_lock.png diff --git a/doc/user/group/img/add_new_members.png b/doc/user/group/img/add_new_members.png Binary files differindex 53f5596de23..99b8e52ea13 100644 --- a/doc/user/group/img/add_new_members.png +++ b/doc/user/group/img/add_new_members.png diff --git a/doc/user/group/img/create_new_group_info.png b/doc/user/group/img/create_new_group_info.png Binary files differindex 8d2501d9f7a..1ac26fb08d9 100644 --- a/doc/user/group/img/create_new_group_info.png +++ b/doc/user/group/img/create_new_group_info.png diff --git a/doc/user/group/img/create_new_project_from_group.png b/doc/user/group/img/create_new_project_from_group.png Binary files differindex c35234660db..553cd0759aa 100644 --- a/doc/user/group/img/create_new_project_from_group.png +++ b/doc/user/group/img/create_new_project_from_group.png diff --git a/doc/user/group/img/group_settings.png b/doc/user/group/img/group_settings.png Binary files differindex 629cd0729aa..1705bf4ce8e 100644 --- a/doc/user/group/img/group_settings.png +++ b/doc/user/group/img/group_settings.png diff --git a/doc/user/group/img/groups.png b/doc/user/group/img/groups.png Binary files differindex 3173ddce7ff..efdfd5f82cd 100644 --- a/doc/user/group/img/groups.png +++ b/doc/user/group/img/groups.png diff --git a/doc/user/group/img/membership_lock.png b/doc/user/group/img/membership_lock.png Binary files differindex d31fbb43375..c9ad82c90f2 100644 --- a/doc/user/group/img/membership_lock.png +++ b/doc/user/group/img/membership_lock.png diff --git a/doc/user/group/img/new_group_form.png b/doc/user/group/img/new_group_form.png Binary files differindex 91727ab5336..1c4d3ec6ceb 100644 --- a/doc/user/group/img/new_group_form.png +++ b/doc/user/group/img/new_group_form.png diff --git a/doc/user/group/img/new_group_from_groups.png b/doc/user/group/img/new_group_from_groups.png Binary files differindex 9c5dd7ebd8b..ffafac1b1cd 100644 --- a/doc/user/group/img/new_group_from_groups.png +++ b/doc/user/group/img/new_group_from_groups.png diff --git a/doc/user/group/img/new_group_from_other_pages.png b/doc/user/group/img/new_group_from_other_pages.png Binary files differindex 77427224447..f84501d1ff2 100644 --- a/doc/user/group/img/new_group_from_other_pages.png +++ b/doc/user/group/img/new_group_from_other_pages.png diff --git a/doc/user/group/img/request_access_button.png b/doc/user/group/img/request_access_button.png Binary files differindex f1aae6afed7..54b490a3bb2 100644 --- a/doc/user/group/img/request_access_button.png +++ b/doc/user/group/img/request_access_button.png diff --git a/doc/user/group/img/select_group_dropdown.png b/doc/user/group/img/select_group_dropdown.png Binary files differindex 68fc950304c..79eca5d94d5 100644 --- a/doc/user/group/img/select_group_dropdown.png +++ b/doc/user/group/img/select_group_dropdown.png diff --git a/doc/user/group/img/share_with_group_lock.png b/doc/user/group/img/share_with_group_lock.png Binary files differindex c0f25389eaf..77b00d8a248 100644 --- a/doc/user/group/img/share_with_group_lock.png +++ b/doc/user/group/img/share_with_group_lock.png diff --git a/doc/user/group/img/withdraw_access_request_button.png b/doc/user/group/img/withdraw_access_request_button.png Binary files differindex c5d8ef6c04f..4365f7fa788 100644 --- a/doc/user/group/img/withdraw_access_request_button.png +++ b/doc/user/group/img/withdraw_access_request_button.png diff --git a/doc/user/group/subgroups/img/group_members.png b/doc/user/group/subgroups/img/group_members.png Binary files differindex b95fe6263bf..830ccafa794 100644 --- a/doc/user/group/subgroups/img/group_members.png +++ b/doc/user/group/subgroups/img/group_members.png diff --git a/doc/user/group/subgroups/img/mention_subgroups.png b/doc/user/group/subgroups/img/mention_subgroups.png Binary files differindex 8e6bed0111b..ec370add4f9 100644 --- a/doc/user/group/subgroups/img/mention_subgroups.png +++ b/doc/user/group/subgroups/img/mention_subgroups.png diff --git a/doc/user/img/award_emoji_comment_picker.png b/doc/user/img/award_emoji_comment_picker.png Binary files differindex 3ad1bab3119..07f90c898ed 100644 --- a/doc/user/img/award_emoji_comment_picker.png +++ b/doc/user/img/award_emoji_comment_picker.png diff --git a/doc/user/img/award_emoji_select.png b/doc/user/img/award_emoji_select.png Binary files differindex 496acb29eec..269282b94b0 100644 --- a/doc/user/img/award_emoji_select.png +++ b/doc/user/img/award_emoji_select.png diff --git a/doc/user/img/award_emoji_votes_sort_options.png b/doc/user/img/award_emoji_votes_sort_options.png Binary files differindex dd84b7f4f64..dc02d5169e0 100644 --- a/doc/user/img/award_emoji_votes_sort_options.png +++ b/doc/user/img/award_emoji_votes_sort_options.png diff --git a/doc/user/img/markdown_logo.png b/doc/user/img/markdown_logo.png Binary files differindex bb3faaaec76..5184851b6cf 100644 --- a/doc/user/img/markdown_logo.png +++ b/doc/user/img/markdown_logo.png diff --git a/doc/user/instance_statistics/img/convdev_index.png b/doc/user/instance_statistics/img/convdev_index.png Binary files differindex 191295c918b..bee1317438d 100644 --- a/doc/user/instance_statistics/img/convdev_index.png +++ b/doc/user/instance_statistics/img/convdev_index.png diff --git a/doc/user/profile/img/active_sessions_list.png b/doc/user/profile/img/active_sessions_list.png Binary files differindex 76a52220bcd..5d94dca69cc 100644 --- a/doc/user/profile/img/active_sessions_list.png +++ b/doc/user/profile/img/active_sessions_list.png diff --git a/doc/user/profile/img/personal_access_tokens.png b/doc/user/profile/img/personal_access_tokens.png Binary files differindex 6aa63dbe342..d29f4cb0a20 100644 --- a/doc/user/profile/img/personal_access_tokens.png +++ b/doc/user/profile/img/personal_access_tokens.png diff --git a/doc/user/profile/img/profil-preferences-navigation-theme.png b/doc/user/profile/img/profil-preferences-navigation-theme.png Binary files differindex 7adaec33b60..335a19ac290 100644 --- a/doc/user/profile/img/profil-preferences-navigation-theme.png +++ b/doc/user/profile/img/profil-preferences-navigation-theme.png diff --git a/doc/user/project/clusters/eks_and_gitlab/img/add_cluster.png b/doc/user/project/clusters/eks_and_gitlab/img/add_cluster.png Binary files differindex 9a0559a19d4..94ec83f1514 100644 --- a/doc/user/project/clusters/eks_and_gitlab/img/add_cluster.png +++ b/doc/user/project/clusters/eks_and_gitlab/img/add_cluster.png diff --git a/doc/user/project/clusters/eks_and_gitlab/img/create_dns.png b/doc/user/project/clusters/eks_and_gitlab/img/create_dns.png Binary files differindex 657ab0d9fa9..61ed85e5cd9 100644 --- a/doc/user/project/clusters/eks_and_gitlab/img/create_dns.png +++ b/doc/user/project/clusters/eks_and_gitlab/img/create_dns.png diff --git a/doc/user/project/clusters/eks_and_gitlab/img/create_project.png b/doc/user/project/clusters/eks_and_gitlab/img/create_project.png Binary files differindex f3446131419..b02ab4b9064 100644 --- a/doc/user/project/clusters/eks_and_gitlab/img/create_project.png +++ b/doc/user/project/clusters/eks_and_gitlab/img/create_project.png diff --git a/doc/user/project/clusters/eks_and_gitlab/img/deploy_apps.png b/doc/user/project/clusters/eks_and_gitlab/img/deploy_apps.png Binary files differindex d6c3b1b3a94..0d9fcc838d9 100644 --- a/doc/user/project/clusters/eks_and_gitlab/img/deploy_apps.png +++ b/doc/user/project/clusters/eks_and_gitlab/img/deploy_apps.png diff --git a/doc/user/project/clusters/eks_and_gitlab/img/environment.png b/doc/user/project/clusters/eks_and_gitlab/img/environment.png Binary files differindex 77d711ba8f6..4714c447026 100644 --- a/doc/user/project/clusters/eks_and_gitlab/img/environment.png +++ b/doc/user/project/clusters/eks_and_gitlab/img/environment.png diff --git a/doc/user/project/clusters/eks_and_gitlab/img/new_project.png b/doc/user/project/clusters/eks_and_gitlab/img/new_project.png Binary files differindex d401c4ac2bf..02afc099f10 100644 --- a/doc/user/project/clusters/eks_and_gitlab/img/new_project.png +++ b/doc/user/project/clusters/eks_and_gitlab/img/new_project.png diff --git a/doc/user/project/clusters/eks_and_gitlab/img/pipeline.png b/doc/user/project/clusters/eks_and_gitlab/img/pipeline.png Binary files differindex 5f9c9815c24..0eb00d0faa7 100644 --- a/doc/user/project/clusters/eks_and_gitlab/img/pipeline.png +++ b/doc/user/project/clusters/eks_and_gitlab/img/pipeline.png diff --git a/doc/user/project/deploy_tokens/img/deploy_tokens.png b/doc/user/project/deploy_tokens/img/deploy_tokens.png Binary files differindex 7e2d67a3120..55c537fd1d3 100644 --- a/doc/user/project/deploy_tokens/img/deploy_tokens.png +++ b/doc/user/project/deploy_tokens/img/deploy_tokens.png diff --git a/doc/user/project/img/bulk-editing.png b/doc/user/project/img/bulk-editing.png Binary files differindex f6b163f55d9..8ae649e5020 100644 --- a/doc/user/project/img/bulk-editing.png +++ b/doc/user/project/img/bulk-editing.png diff --git a/doc/user/project/img/cycle_analytics_landing_page.png b/doc/user/project/img/cycle_analytics_landing_page.png Binary files differindex 316612c0da0..8b17fae5e05 100644 --- a/doc/user/project/img/cycle_analytics_landing_page.png +++ b/doc/user/project/img/cycle_analytics_landing_page.png diff --git a/doc/user/project/img/issue_board_assignee_lists.png b/doc/user/project/img/issue_board_assignee_lists.png Binary files differindex 1ec94d22e33..f2660cd8f80 100644 --- a/doc/user/project/img/issue_board_assignee_lists.png +++ b/doc/user/project/img/issue_board_assignee_lists.png diff --git a/doc/user/project/img/issue_board_creation.png b/doc/user/project/img/issue_board_creation.png Binary files differindex 9dc4925b0a5..099fe6eee21 100644 --- a/doc/user/project/img/issue_board_creation.png +++ b/doc/user/project/img/issue_board_creation.png diff --git a/doc/user/project/img/issue_board_edit_button.png b/doc/user/project/img/issue_board_edit_button.png Binary files differindex 23883175344..a0dc6f41592 100644 --- a/doc/user/project/img/issue_board_edit_button.png +++ b/doc/user/project/img/issue_board_edit_button.png diff --git a/doc/user/project/img/issue_board_move_issue_card_list.png b/doc/user/project/img/issue_board_move_issue_card_list.png Binary files differindex cce252234c1..13750a63766 100644 --- a/doc/user/project/img/issue_board_move_issue_card_list.png +++ b/doc/user/project/img/issue_board_move_issue_card_list.png diff --git a/doc/user/project/img/issue_board_view_scope.png b/doc/user/project/img/issue_board_view_scope.png Binary files differindex 4e03cecbc2d..d173679a0e7 100644 --- a/doc/user/project/img/issue_board_view_scope.png +++ b/doc/user/project/img/issue_board_view_scope.png diff --git a/doc/user/project/img/issue_boards_add_issues_modal.png b/doc/user/project/img/issue_boards_add_issues_modal.png Binary files differindex 625a4304eaf..ecddf6709d0 100644 --- a/doc/user/project/img/issue_boards_add_issues_modal.png +++ b/doc/user/project/img/issue_boards_add_issues_modal.png diff --git a/doc/user/project/img/issue_boards_multiple.png b/doc/user/project/img/issue_boards_multiple.png Binary files differindex 4b2b8d457f1..7bb088aad0b 100644 --- a/doc/user/project/img/issue_boards_multiple.png +++ b/doc/user/project/img/issue_boards_multiple.png diff --git a/doc/user/project/img/issue_boards_remove_issue.png b/doc/user/project/img/issue_boards_remove_issue.png Binary files differindex 9a2fad2cc7f..7050e6c3ede 100644 --- a/doc/user/project/img/issue_boards_remove_issue.png +++ b/doc/user/project/img/issue_boards_remove_issue.png diff --git a/doc/user/project/img/koding_build-in-progress.png b/doc/user/project/img/koding_build-in-progress.png Binary files differindex 79b7b2f10a2..118b97c07e1 100644 --- a/doc/user/project/img/koding_build-in-progress.png +++ b/doc/user/project/img/koding_build-in-progress.png diff --git a/doc/user/project/img/koding_build-success.png b/doc/user/project/img/koding_build-success.png Binary files differindex a2342cfd324..0f3b954abf5 100644 --- a/doc/user/project/img/koding_build-success.png +++ b/doc/user/project/img/koding_build-success.png diff --git a/doc/user/project/img/koding_commit-koding.yml.png b/doc/user/project/img/koding_commit-koding.yml.png Binary files differindex 16842410ae2..d921c73dc73 100644 --- a/doc/user/project/img/koding_commit-koding.yml.png +++ b/doc/user/project/img/koding_commit-koding.yml.png diff --git a/doc/user/project/img/koding_edit-on-ide.png b/doc/user/project/img/koding_edit-on-ide.png Binary files differindex ab861281d3e..25ca7694fe0 100644 --- a/doc/user/project/img/koding_edit-on-ide.png +++ b/doc/user/project/img/koding_edit-on-ide.png diff --git a/doc/user/project/img/koding_enable-koding.png b/doc/user/project/img/koding_enable-koding.png Binary files differindex 0b6fcfadcc5..7e6c1735df2 100644 --- a/doc/user/project/img/koding_enable-koding.png +++ b/doc/user/project/img/koding_enable-koding.png diff --git a/doc/user/project/img/koding_landing.png b/doc/user/project/img/koding_landing.png Binary files differindex 1eeddcd3813..ac880376e09 100644 --- a/doc/user/project/img/koding_landing.png +++ b/doc/user/project/img/koding_landing.png diff --git a/doc/user/project/img/koding_run-in-ide.png b/doc/user/project/img/koding_run-in-ide.png Binary files differindex d22e5023c59..fb5825a4010 100644 --- a/doc/user/project/img/koding_run-in-ide.png +++ b/doc/user/project/img/koding_run-in-ide.png diff --git a/doc/user/project/img/koding_stack-import.png b/doc/user/project/img/koding_stack-import.png Binary files differindex 245ccb07ba3..483bfad7d6a 100644 --- a/doc/user/project/img/koding_stack-import.png +++ b/doc/user/project/img/koding_stack-import.png diff --git a/doc/user/project/img/koding_start-build.png b/doc/user/project/img/koding_start-build.png Binary files differindex 3f5c16d5d2f..c09a6d669f0 100644 --- a/doc/user/project/img/koding_start-build.png +++ b/doc/user/project/img/koding_start-build.png diff --git a/doc/user/project/img/labels_generate_default.png b/doc/user/project/img/labels_generate_default.png Binary files differindex fca2a06e04f..982a4df999c 100644 --- a/doc/user/project/img/labels_generate_default.png +++ b/doc/user/project/img/labels_generate_default.png diff --git a/doc/user/project/img/labels_group_issues.png b/doc/user/project/img/labels_group_issues.png Binary files differindex 29dcf7ff45e..cea1d304d31 100644 --- a/doc/user/project/img/labels_group_issues.png +++ b/doc/user/project/img/labels_group_issues.png diff --git a/doc/user/project/img/labels_list.png b/doc/user/project/img/labels_list.png Binary files differindex 12c47ea9766..6878349fc0c 100644 --- a/doc/user/project/img/labels_list.png +++ b/doc/user/project/img/labels_list.png diff --git a/doc/user/project/img/labels_prioritized.png b/doc/user/project/img/labels_prioritized.png Binary files differindex 57dcfe89b3d..7ce2d08b38c 100644 --- a/doc/user/project/img/labels_prioritized.png +++ b/doc/user/project/img/labels_prioritized.png diff --git a/doc/user/project/img/labels_project_list_search.png b/doc/user/project/img/labels_project_list_search.png Binary files differindex ff9bf92e1c3..512d7767e6e 100644 --- a/doc/user/project/img/labels_project_list_search.png +++ b/doc/user/project/img/labels_project_list_search.png diff --git a/doc/user/project/img/labels_promotion.png b/doc/user/project/img/labels_promotion.png Binary files differindex 8a5efd210a2..762a3773692 100644 --- a/doc/user/project/img/labels_promotion.png +++ b/doc/user/project/img/labels_promotion.png diff --git a/doc/user/project/img/labels_sidebar.png b/doc/user/project/img/labels_sidebar.png Binary files differindex 7349c6d4f0c..454a0ca3f07 100644 --- a/doc/user/project/img/labels_sidebar.png +++ b/doc/user/project/img/labels_sidebar.png diff --git a/doc/user/project/img/labels_sidebar_assign.png b/doc/user/project/img/labels_sidebar_assign.png Binary files differindex 61e8d04fc85..5b7fb78b032 100644 --- a/doc/user/project/img/labels_sidebar_assign.png +++ b/doc/user/project/img/labels_sidebar_assign.png diff --git a/doc/user/project/img/labels_sidebar_inline.png b/doc/user/project/img/labels_sidebar_inline.png Binary files differindex 31fa397761d..2186f14ea92 100644 --- a/doc/user/project/img/labels_sidebar_inline.png +++ b/doc/user/project/img/labels_sidebar_inline.png diff --git a/doc/user/project/img/labels_sort_label_priority.png b/doc/user/project/img/labels_sort_label_priority.png Binary files differindex c8b97639121..faf629ac61d 100644 --- a/doc/user/project/img/labels_sort_label_priority.png +++ b/doc/user/project/img/labels_sort_label_priority.png diff --git a/doc/user/project/img/labels_sort_priority.png b/doc/user/project/img/labels_sort_priority.png Binary files differindex a95198e7f72..a6b5fca26f4 100644 --- a/doc/user/project/img/labels_sort_priority.png +++ b/doc/user/project/img/labels_sort_priority.png diff --git a/doc/user/project/img/labels_subscriptions.png b/doc/user/project/img/labels_subscriptions.png Binary files differindex 8bcb3b57f6c..f3c4235d051 100644 --- a/doc/user/project/img/labels_subscriptions.png +++ b/doc/user/project/img/labels_subscriptions.png diff --git a/doc/user/project/img/priority_sort_order.png b/doc/user/project/img/priority_sort_order.png Binary files differindex c558ec23b0e..cd1dd8237c0 100644 --- a/doc/user/project/img/priority_sort_order.png +++ b/doc/user/project/img/priority_sort_order.png diff --git a/doc/user/project/img/project_overview_badges.png b/doc/user/project/img/project_overview_badges.png Binary files differindex 3067a7dfa13..83b9766828a 100644 --- a/doc/user/project/img/project_overview_badges.png +++ b/doc/user/project/img/project_overview_badges.png diff --git a/doc/user/project/img/project_repository_settings.png b/doc/user/project/img/project_repository_settings.png Binary files differindex aa4d4452c87..69d36753a58 100644 --- a/doc/user/project/img/project_repository_settings.png +++ b/doc/user/project/img/project_repository_settings.png diff --git a/doc/user/project/img/protected_branches_delete.png b/doc/user/project/img/protected_branches_delete.png Binary files differindex cfdfe6c6c29..8910ae9e39d 100644 --- a/doc/user/project/img/protected_branches_delete.png +++ b/doc/user/project/img/protected_branches_delete.png diff --git a/doc/user/project/img/protected_branches_devs_can_push.png b/doc/user/project/img/protected_branches_devs_can_push.png Binary files differindex 320e6eb7fee..b537839c00b 100644 --- a/doc/user/project/img/protected_branches_devs_can_push.png +++ b/doc/user/project/img/protected_branches_devs_can_push.png diff --git a/doc/user/project/img/protected_branches_error_ui.png b/doc/user/project/img/protected_branches_error_ui.png Binary files differindex 3f8e462d3ad..62839e49d89 100644 --- a/doc/user/project/img/protected_branches_error_ui.png +++ b/doc/user/project/img/protected_branches_error_ui.png diff --git a/doc/user/project/img/protected_branches_list.png b/doc/user/project/img/protected_branches_list.png Binary files differindex 1b2936cb711..495ce4d7b6f 100644 --- a/doc/user/project/img/protected_branches_list.png +++ b/doc/user/project/img/protected_branches_list.png diff --git a/doc/user/project/img/protected_branches_page.png b/doc/user/project/img/protected_branches_page.png Binary files differindex 4e5afff3bae..9b10991f62e 100644 --- a/doc/user/project/img/protected_branches_page.png +++ b/doc/user/project/img/protected_branches_page.png diff --git a/doc/user/project/img/protected_tag_matches.png b/doc/user/project/img/protected_tag_matches.png Binary files differindex a36a11a1271..e89d0a47073 100644 --- a/doc/user/project/img/protected_tag_matches.png +++ b/doc/user/project/img/protected_tag_matches.png diff --git a/doc/user/project/img/protected_tags_list.png b/doc/user/project/img/protected_tags_list.png Binary files differindex c5e42dc0705..6c5295e0f4b 100644 --- a/doc/user/project/img/protected_tags_list.png +++ b/doc/user/project/img/protected_tags_list.png diff --git a/doc/user/project/img/protected_tags_page.png b/doc/user/project/img/protected_tags_page.png Binary files differindex 3848d91ebd6..5f8a2106cd1 100644 --- a/doc/user/project/img/protected_tags_page.png +++ b/doc/user/project/img/protected_tags_page.png diff --git a/doc/user/project/img/protected_tags_permissions_dropdown.png b/doc/user/project/img/protected_tags_permissions_dropdown.png Binary files differindex 9e0fc4e2a43..77098eeb591 100644 --- a/doc/user/project/img/protected_tags_permissions_dropdown.png +++ b/doc/user/project/img/protected_tags_permissions_dropdown.png diff --git a/doc/user/project/import/img/bitbucket_server_import_credentials.png b/doc/user/project/import/img/bitbucket_server_import_credentials.png Binary files differindex 70b26e89d49..25bcc3ab6e6 100644 --- a/doc/user/project/import/img/bitbucket_server_import_credentials.png +++ b/doc/user/project/import/img/bitbucket_server_import_credentials.png diff --git a/doc/user/project/import/img/bitbucket_server_import_select_project.png b/doc/user/project/import/img/bitbucket_server_import_select_project.png Binary files differindex e5b1b89e6a3..e7fddef9955 100644 --- a/doc/user/project/import/img/bitbucket_server_import_select_project.png +++ b/doc/user/project/import/img/bitbucket_server_import_select_project.png diff --git a/doc/user/project/import/img/fogbugz_import_login.png b/doc/user/project/import/img/fogbugz_import_login.png Binary files differindex 96bce70b74d..6ba4d443f1a 100644 --- a/doc/user/project/import/img/fogbugz_import_login.png +++ b/doc/user/project/import/img/fogbugz_import_login.png diff --git a/doc/user/project/import/img/fogbugz_import_select_fogbogz.png b/doc/user/project/import/img/fogbugz_import_select_fogbogz.png Binary files differindex b26c652e382..d207646a6f2 100644 --- a/doc/user/project/import/img/fogbugz_import_select_fogbogz.png +++ b/doc/user/project/import/img/fogbugz_import_select_fogbogz.png diff --git a/doc/user/project/import/img/fogbugz_import_select_project.png b/doc/user/project/import/img/fogbugz_import_select_project.png Binary files differindex ccc82f9d4cd..b5e6f497f9b 100644 --- a/doc/user/project/import/img/fogbugz_import_select_project.png +++ b/doc/user/project/import/img/fogbugz_import_select_project.png diff --git a/doc/user/project/import/img/import_projects_from_gitea_new_import.png b/doc/user/project/import/img/import_projects_from_gitea_new_import.png Binary files differindex a3f603cbd0a..41eb4b2bd00 100644 --- a/doc/user/project/import/img/import_projects_from_gitea_new_import.png +++ b/doc/user/project/import/img/import_projects_from_gitea_new_import.png diff --git a/doc/user/project/import/img/import_projects_from_github_select_auth_method.png b/doc/user/project/import/img/import_projects_from_github_select_auth_method.png Binary files differindex 1ccb38a815e..90e6243aec0 100644 --- a/doc/user/project/import/img/import_projects_from_github_select_auth_method.png +++ b/doc/user/project/import/img/import_projects_from_github_select_auth_method.png diff --git a/doc/user/project/import/img/import_projects_from_new_project_page.png b/doc/user/project/import/img/import_projects_from_new_project_page.png Binary files differindex 40402eae226..7c32d3555d1 100644 --- a/doc/user/project/import/img/import_projects_from_new_project_page.png +++ b/doc/user/project/import/img/import_projects_from_new_project_page.png diff --git a/doc/user/project/integrations/img/hangouts_chat_configuration.png b/doc/user/project/integrations/img/hangouts_chat_configuration.png Binary files differindex 33fadbe6547..54aaef6632d 100644 --- a/doc/user/project/integrations/img/hangouts_chat_configuration.png +++ b/doc/user/project/integrations/img/hangouts_chat_configuration.png diff --git a/doc/user/project/integrations/img/issue_configuration.png b/doc/user/project/integrations/img/issue_configuration.png Binary files differindex 2049d60fdd2..5dfd85974d8 100644 --- a/doc/user/project/integrations/img/issue_configuration.png +++ b/doc/user/project/integrations/img/issue_configuration.png diff --git a/doc/user/project/integrations/img/jira_group_access.png b/doc/user/project/integrations/img/jira_group_access.png Binary files differindex 9d64cc57269..448cc55504d 100644 --- a/doc/user/project/integrations/img/jira_group_access.png +++ b/doc/user/project/integrations/img/jira_group_access.png diff --git a/doc/user/project/integrations/img/jira_project_name.png b/doc/user/project/integrations/img/jira_project_name.png Binary files differindex 8540a427461..981c7f7ca18 100644 --- a/doc/user/project/integrations/img/jira_project_name.png +++ b/doc/user/project/integrations/img/jira_project_name.png diff --git a/doc/user/project/integrations/img/jira_project_settings.png b/doc/user/project/integrations/img/jira_project_settings.png Binary files differindex cb6a6ba14ce..d96002b7db8 100644 --- a/doc/user/project/integrations/img/jira_project_settings.png +++ b/doc/user/project/integrations/img/jira_project_settings.png diff --git a/doc/user/project/integrations/img/jira_service.png b/doc/user/project/integrations/img/jira_service.png Binary files differindex 8e073b84ff9..0ae2fa28756 100644 --- a/doc/user/project/integrations/img/jira_service.png +++ b/doc/user/project/integrations/img/jira_service.png diff --git a/doc/user/project/integrations/img/jira_service_close_comment.png b/doc/user/project/integrations/img/jira_service_close_comment.png Binary files differindex bb9cd7e3d13..9af0d38f098 100644 --- a/doc/user/project/integrations/img/jira_service_close_comment.png +++ b/doc/user/project/integrations/img/jira_service_close_comment.png diff --git a/doc/user/project/integrations/img/jira_service_page.png b/doc/user/project/integrations/img/jira_service_page.png Binary files differindex 63aa0e99a50..c75c11888a8 100644 --- a/doc/user/project/integrations/img/jira_service_page.png +++ b/doc/user/project/integrations/img/jira_service_page.png diff --git a/doc/user/project/integrations/img/jira_user_management_link.png b/doc/user/project/integrations/img/jira_user_management_link.png Binary files differindex f81c5b5fc87..5eb9d031c3e 100644 --- a/doc/user/project/integrations/img/jira_user_management_link.png +++ b/doc/user/project/integrations/img/jira_user_management_link.png diff --git a/doc/user/project/integrations/img/mattermost_bot_auth.png b/doc/user/project/integrations/img/mattermost_bot_auth.png Binary files differindex 830b7849f3d..a05d8da1237 100644 --- a/doc/user/project/integrations/img/mattermost_bot_auth.png +++ b/doc/user/project/integrations/img/mattermost_bot_auth.png diff --git a/doc/user/project/integrations/img/mattermost_bot_available_commands.png b/doc/user/project/integrations/img/mattermost_bot_available_commands.png Binary files differindex b51798cf10d..3232ccc3451 100644 --- a/doc/user/project/integrations/img/mattermost_bot_available_commands.png +++ b/doc/user/project/integrations/img/mattermost_bot_available_commands.png diff --git a/doc/user/project/integrations/img/mattermost_configuration.png b/doc/user/project/integrations/img/mattermost_configuration.png Binary files differindex f52acf4ef3b..e0b55b23520 100644 --- a/doc/user/project/integrations/img/mattermost_configuration.png +++ b/doc/user/project/integrations/img/mattermost_configuration.png diff --git a/doc/user/project/integrations/img/mattermost_console_integrations.png b/doc/user/project/integrations/img/mattermost_console_integrations.png Binary files differindex 92a30da5be0..625b57d4dc9 100644 --- a/doc/user/project/integrations/img/mattermost_console_integrations.png +++ b/doc/user/project/integrations/img/mattermost_console_integrations.png diff --git a/doc/user/project/integrations/img/mattermost_gitlab_token.png b/doc/user/project/integrations/img/mattermost_gitlab_token.png Binary files differindex 257018914d2..63140503824 100644 --- a/doc/user/project/integrations/img/mattermost_gitlab_token.png +++ b/doc/user/project/integrations/img/mattermost_gitlab_token.png diff --git a/doc/user/project/integrations/img/mattermost_goto_console.png b/doc/user/project/integrations/img/mattermost_goto_console.png Binary files differindex 3354c2a24b4..8bacbe485f4 100644 --- a/doc/user/project/integrations/img/mattermost_goto_console.png +++ b/doc/user/project/integrations/img/mattermost_goto_console.png diff --git a/doc/user/project/integrations/img/mattermost_slash_command_configuration.png b/doc/user/project/integrations/img/mattermost_slash_command_configuration.png Binary files differindex 12766ab2b34..f9e9de439ca 100644 --- a/doc/user/project/integrations/img/mattermost_slash_command_configuration.png +++ b/doc/user/project/integrations/img/mattermost_slash_command_configuration.png diff --git a/doc/user/project/integrations/img/mattermost_team_integrations.png b/doc/user/project/integrations/img/mattermost_team_integrations.png Binary files differindex 69d4a231e5a..c2b68256e11 100644 --- a/doc/user/project/integrations/img/mattermost_team_integrations.png +++ b/doc/user/project/integrations/img/mattermost_team_integrations.png diff --git a/doc/user/project/integrations/img/merge_request_performance.png b/doc/user/project/integrations/img/merge_request_performance.png Binary files differindex eba6515a6ae..a9cd761cdcb 100644 --- a/doc/user/project/integrations/img/merge_request_performance.png +++ b/doc/user/project/integrations/img/merge_request_performance.png diff --git a/doc/user/project/integrations/img/microsoft_teams_configuration.png b/doc/user/project/integrations/img/microsoft_teams_configuration.png Binary files differindex b5c9efc3dd9..627715d5c18 100644 --- a/doc/user/project/integrations/img/microsoft_teams_configuration.png +++ b/doc/user/project/integrations/img/microsoft_teams_configuration.png diff --git a/doc/user/project/integrations/img/project_services.png b/doc/user/project/integrations/img/project_services.png Binary files differindex 25b6cd5690b..5fed38a349c 100644 --- a/doc/user/project/integrations/img/project_services.png +++ b/doc/user/project/integrations/img/project_services.png diff --git a/doc/user/project/integrations/img/prometheus_dashboard.png b/doc/user/project/integrations/img/prometheus_dashboard.png Binary files differindex bd19f1b44cc..1fa36ca2675 100644 --- a/doc/user/project/integrations/img/prometheus_dashboard.png +++ b/doc/user/project/integrations/img/prometheus_dashboard.png diff --git a/doc/user/project/integrations/img/prometheus_deploy.png b/doc/user/project/integrations/img/prometheus_deploy.png Binary files differindex d39081bcc7b..3f19f23b0cc 100644 --- a/doc/user/project/integrations/img/prometheus_deploy.png +++ b/doc/user/project/integrations/img/prometheus_deploy.png diff --git a/doc/user/project/integrations/img/prometheus_yaml_deploy.png b/doc/user/project/integrations/img/prometheus_yaml_deploy.png Binary files differindex 978cd7eaa50..78dd178a077 100644 --- a/doc/user/project/integrations/img/prometheus_yaml_deploy.png +++ b/doc/user/project/integrations/img/prometheus_yaml_deploy.png diff --git a/doc/user/project/integrations/img/redmine_configuration.png b/doc/user/project/integrations/img/redmine_configuration.png Binary files differindex 7b6dd271401..eb392b848b5 100644 --- a/doc/user/project/integrations/img/redmine_configuration.png +++ b/doc/user/project/integrations/img/redmine_configuration.png diff --git a/doc/user/project/integrations/img/services_templates_redmine_example.png b/doc/user/project/integrations/img/services_templates_redmine_example.png Binary files differindex 379cef9888d..34594dfdd55 100644 --- a/doc/user/project/integrations/img/services_templates_redmine_example.png +++ b/doc/user/project/integrations/img/services_templates_redmine_example.png diff --git a/doc/user/project/integrations/img/slack_configuration.png b/doc/user/project/integrations/img/slack_configuration.png Binary files differindex 527824fc3eb..53b30e0e8cd 100644 --- a/doc/user/project/integrations/img/slack_configuration.png +++ b/doc/user/project/integrations/img/slack_configuration.png diff --git a/doc/user/project/integrations/img/webhook_logs.png b/doc/user/project/integrations/img/webhook_logs.png Binary files differindex 803678db6b6..24bb593c7d0 100644 --- a/doc/user/project/integrations/img/webhook_logs.png +++ b/doc/user/project/integrations/img/webhook_logs.png diff --git a/doc/user/project/integrations/img/webhook_testing.png b/doc/user/project/integrations/img/webhook_testing.png Binary files differindex 176dcec9d8a..acfebf473b9 100644 --- a/doc/user/project/integrations/img/webhook_testing.png +++ b/doc/user/project/integrations/img/webhook_testing.png diff --git a/doc/user/project/integrations/img/webhooks_ssl.png b/doc/user/project/integrations/img/webhooks_ssl.png Binary files differindex 21ddec4ebdf..f023e9665f2 100644 --- a/doc/user/project/integrations/img/webhooks_ssl.png +++ b/doc/user/project/integrations/img/webhooks_ssl.png diff --git a/doc/user/project/issues/img/confidential_issues_index_page.png b/doc/user/project/issues/img/confidential_issues_index_page.png Binary files differindex f3efe0ce04e..16979bf9ac2 100644 --- a/doc/user/project/issues/img/confidential_issues_index_page.png +++ b/doc/user/project/issues/img/confidential_issues_index_page.png diff --git a/doc/user/project/issues/img/delete_issue.png b/doc/user/project/issues/img/delete_issue.png Binary files differindex a356f52044e..87ea65956fc 100644 --- a/doc/user/project/issues/img/delete_issue.png +++ b/doc/user/project/issues/img/delete_issue.png diff --git a/doc/user/project/issues/img/due_dates_create.png b/doc/user/project/issues/img/due_dates_create.png Binary files differindex ece35d44213..392fb3553cb 100644 --- a/doc/user/project/issues/img/due_dates_create.png +++ b/doc/user/project/issues/img/due_dates_create.png diff --git a/doc/user/project/issues/img/group_issues_list_view.png b/doc/user/project/issues/img/group_issues_list_view.png Binary files differindex bba964076d0..c951a9e2dcd 100644 --- a/doc/user/project/issues/img/group_issues_list_view.png +++ b/doc/user/project/issues/img/group_issues_list_view.png diff --git a/doc/user/project/issues/img/issue_board.png b/doc/user/project/issues/img/issue_board.png Binary files differindex 87b1016cc76..df9d6f64985 100644 --- a/doc/user/project/issues/img/issue_board.png +++ b/doc/user/project/issues/img/issue_board.png diff --git a/doc/user/project/issues/img/issue_template.png b/doc/user/project/issues/img/issue_template.png Binary files differindex 0e4c8df897b..6cb2c07d27e 100644 --- a/doc/user/project/issues/img/issue_template.png +++ b/doc/user/project/issues/img/issue_template.png diff --git a/doc/user/project/issues/img/new_issue_from_email.png b/doc/user/project/issues/img/new_issue_from_email.png Binary files differindex 775ea0cdffb..6da899ea37c 100644 --- a/doc/user/project/issues/img/new_issue_from_email.png +++ b/doc/user/project/issues/img/new_issue_from_email.png diff --git a/doc/user/project/issues/img/new_issue_from_issue_board.png b/doc/user/project/issues/img/new_issue_from_issue_board.png Binary files differindex da892eff0a6..30a1ffb9011 100644 --- a/doc/user/project/issues/img/new_issue_from_issue_board.png +++ b/doc/user/project/issues/img/new_issue_from_issue_board.png diff --git a/doc/user/project/issues/img/new_issue_from_projects_dashboard.png b/doc/user/project/issues/img/new_issue_from_projects_dashboard.png Binary files differindex 4b9535f6b15..474ca2b45c0 100644 --- a/doc/user/project/issues/img/new_issue_from_projects_dashboard.png +++ b/doc/user/project/issues/img/new_issue_from_projects_dashboard.png diff --git a/doc/user/project/issues/img/project_issues_list_view.png b/doc/user/project/issues/img/project_issues_list_view.png Binary files differindex 584a81aab8a..c80bd58f5c9 100644 --- a/doc/user/project/issues/img/project_issues_list_view.png +++ b/doc/user/project/issues/img/project_issues_list_view.png diff --git a/doc/user/project/issues/img/sidebar_confidential_issue.png b/doc/user/project/issues/img/sidebar_confidential_issue.png Binary files differindex d99a1ca756e..a320f4dcfe5 100644 --- a/doc/user/project/issues/img/sidebar_confidential_issue.png +++ b/doc/user/project/issues/img/sidebar_confidential_issue.png diff --git a/doc/user/project/issues/img/sidebar_move_issue.png b/doc/user/project/issues/img/sidebar_move_issue.png Binary files differindex 1e688cec894..031284a24b2 100644 --- a/doc/user/project/issues/img/sidebar_move_issue.png +++ b/doc/user/project/issues/img/sidebar_move_issue.png diff --git a/doc/user/project/issues/img/sidebar_not_confidential_issue.png b/doc/user/project/issues/img/sidebar_not_confidential_issue.png Binary files differindex 2e6cbbc5b3a..c09f8204b37 100644 --- a/doc/user/project/issues/img/sidebar_not_confidential_issue.png +++ b/doc/user/project/issues/img/sidebar_not_confidential_issue.png diff --git a/doc/user/project/issues/img/turn_off_confidentiality.png b/doc/user/project/issues/img/turn_off_confidentiality.png Binary files differindex 248ae6522d6..04a85933071 100644 --- a/doc/user/project/issues/img/turn_off_confidentiality.png +++ b/doc/user/project/issues/img/turn_off_confidentiality.png diff --git a/doc/user/project/issues/img/turn_on_confidentiality.png b/doc/user/project/issues/img/turn_on_confidentiality.png Binary files differindex fac4c833699..fac360ca6dc 100644 --- a/doc/user/project/issues/img/turn_on_confidentiality.png +++ b/doc/user/project/issues/img/turn_on_confidentiality.png diff --git a/doc/user/project/members/img/access_requests_management.png b/doc/user/project/members/img/access_requests_management.png Binary files differindex 3693bed869b..8996d9564d7 100644 --- a/doc/user/project/members/img/access_requests_management.png +++ b/doc/user/project/members/img/access_requests_management.png diff --git a/doc/user/project/members/img/add_new_user_to_project_settings.png b/doc/user/project/members/img/add_new_user_to_project_settings.png Binary files differindex 40db600455f..e49ea1a3e3d 100644 --- a/doc/user/project/members/img/add_new_user_to_project_settings.png +++ b/doc/user/project/members/img/add_new_user_to_project_settings.png diff --git a/doc/user/project/members/img/add_user_email_accept.png b/doc/user/project/members/img/add_user_email_accept.png Binary files differindex 763b3ff463d..cbee9e08c70 100644 --- a/doc/user/project/members/img/add_user_email_accept.png +++ b/doc/user/project/members/img/add_user_email_accept.png diff --git a/doc/user/project/members/img/add_user_import_members_from_another_project.png b/doc/user/project/members/img/add_user_import_members_from_another_project.png Binary files differindex 0c32001098e..cb3b70bd4b5 100644 --- a/doc/user/project/members/img/add_user_import_members_from_another_project.png +++ b/doc/user/project/members/img/add_user_import_members_from_another_project.png diff --git a/doc/user/project/members/img/add_user_members_menu.png b/doc/user/project/members/img/add_user_members_menu.png Binary files differindex 8e61d15fe65..6f08088b52f 100644 --- a/doc/user/project/members/img/add_user_members_menu.png +++ b/doc/user/project/members/img/add_user_members_menu.png diff --git a/doc/user/project/members/img/max_access_level.png b/doc/user/project/members/img/max_access_level.png Binary files differindex 63f33f9d91d..42a0416ffbb 100644 --- a/doc/user/project/members/img/max_access_level.png +++ b/doc/user/project/members/img/max_access_level.png diff --git a/doc/user/project/members/img/request_access_button.png b/doc/user/project/members/img/request_access_button.png Binary files differindex 608baccb0ca..e8b490b91b8 100644 --- a/doc/user/project/members/img/request_access_button.png +++ b/doc/user/project/members/img/request_access_button.png diff --git a/doc/user/project/members/img/withdraw_access_request_button.png b/doc/user/project/members/img/withdraw_access_request_button.png Binary files differindex 6edd786b151..6a3172dfcdb 100644 --- a/doc/user/project/members/img/withdraw_access_request_button.png +++ b/doc/user/project/members/img/withdraw_access_request_button.png diff --git a/doc/user/project/merge_requests/img/allow_collaboration.png b/doc/user/project/merge_requests/img/allow_collaboration.png Binary files differindex 75596e7d9ad..3c81e4c27b8 100644 --- a/doc/user/project/merge_requests/img/allow_collaboration.png +++ b/doc/user/project/merge_requests/img/allow_collaboration.png diff --git a/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png b/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png Binary files differindex 7dc344f8cf6..c98821548f8 100644 --- a/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png +++ b/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png diff --git a/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png b/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png Binary files differindex 811b0998f85..8b51503419b 100644 --- a/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png +++ b/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png diff --git a/doc/user/project/merge_requests/img/create_from_email.png b/doc/user/project/merge_requests/img/create_from_email.png Binary files differindex 71eb4bf267d..610f0b3d0c1 100644 --- a/doc/user/project/merge_requests/img/create_from_email.png +++ b/doc/user/project/merge_requests/img/create_from_email.png diff --git a/doc/user/project/merge_requests/img/merge_conflict_editor.png b/doc/user/project/merge_requests/img/merge_conflict_editor.png Binary files differindex 6660920c191..f10efbce5f5 100644 --- a/doc/user/project/merge_requests/img/merge_conflict_editor.png +++ b/doc/user/project/merge_requests/img/merge_conflict_editor.png diff --git a/doc/user/project/merge_requests/img/merge_request.png b/doc/user/project/merge_requests/img/merge_request.png Binary files differindex 61b61122b11..c0a62bbaba0 100644 --- a/doc/user/project/merge_requests/img/merge_request.png +++ b/doc/user/project/merge_requests/img/merge_request.png diff --git a/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png b/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png Binary files differindex 4eee734ff8d..ac766c99935 100644 --- a/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png +++ b/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png diff --git a/doc/user/project/merge_requests/img/merge_request_widget.png b/doc/user/project/merge_requests/img/merge_request_widget.png Binary files differindex 43a945c74d9..6c2317b29b5 100644 --- a/doc/user/project/merge_requests/img/merge_request_widget.png +++ b/doc/user/project/merge_requests/img/merge_request_widget.png diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png Binary files differindex d7f0535d3c5..9487264b41a 100644 --- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png +++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png Binary files differindex c43f76b058c..761690d1e0c 100644 --- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png +++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png Binary files differindex 9629ed99838..2a2101719ba 100644 --- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png +++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png Binary files differindex d0691437c65..70fa2efc855 100644 --- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png +++ b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png diff --git a/doc/user/project/merge_requests/img/project_merge_requests_list_view.png b/doc/user/project/merge_requests/img/project_merge_requests_list_view.png Binary files differindex 702ec1a2949..457716d811c 100644 --- a/doc/user/project/merge_requests/img/project_merge_requests_list_view.png +++ b/doc/user/project/merge_requests/img/project_merge_requests_list_view.png diff --git a/doc/user/project/merge_requests/img/remove_source_branch_status.png b/doc/user/project/merge_requests/img/remove_source_branch_status.png Binary files differindex 1377fab54ec..afd93207e02 100644 --- a/doc/user/project/merge_requests/img/remove_source_branch_status.png +++ b/doc/user/project/merge_requests/img/remove_source_branch_status.png diff --git a/doc/user/project/merge_requests/img/revert_changes_commit.png b/doc/user/project/merge_requests/img/revert_changes_commit.png Binary files differindex a0663e130e9..c9dd0019024 100644 --- a/doc/user/project/merge_requests/img/revert_changes_commit.png +++ b/doc/user/project/merge_requests/img/revert_changes_commit.png diff --git a/doc/user/project/merge_requests/img/revert_changes_mr.png b/doc/user/project/merge_requests/img/revert_changes_mr.png Binary files differindex 8792018ee53..06b841b3002 100644 --- a/doc/user/project/merge_requests/img/revert_changes_mr.png +++ b/doc/user/project/merge_requests/img/revert_changes_mr.png diff --git a/doc/user/project/merge_requests/img/squash_edit_form.png b/doc/user/project/merge_requests/img/squash_edit_form.png Binary files differindex 496c6f44ea7..326d74b68cb 100644 --- a/doc/user/project/merge_requests/img/squash_edit_form.png +++ b/doc/user/project/merge_requests/img/squash_edit_form.png diff --git a/doc/user/project/merge_requests/img/squash_mr_commits.png b/doc/user/project/merge_requests/img/squash_mr_commits.png Binary files differindex 5fc6a8c48bb..dfc1ee38435 100644 --- a/doc/user/project/merge_requests/img/squash_mr_commits.png +++ b/doc/user/project/merge_requests/img/squash_mr_commits.png diff --git a/doc/user/project/merge_requests/img/squash_mr_widget.png b/doc/user/project/merge_requests/img/squash_mr_widget.png Binary files differindex 9cb458b2a35..81334ca9758 100644 --- a/doc/user/project/merge_requests/img/squash_mr_widget.png +++ b/doc/user/project/merge_requests/img/squash_mr_widget.png diff --git a/doc/user/project/merge_requests/img/squash_squashed_commit.png b/doc/user/project/merge_requests/img/squash_squashed_commit.png Binary files differindex 0cf5875f82c..458361c5490 100644 --- a/doc/user/project/merge_requests/img/squash_squashed_commit.png +++ b/doc/user/project/merge_requests/img/squash_squashed_commit.png diff --git a/doc/user/project/merge_requests/img/versions.png b/doc/user/project/merge_requests/img/versions.png Binary files differindex 3883fb4bc1c..8355fd62dcb 100644 --- a/doc/user/project/merge_requests/img/versions.png +++ b/doc/user/project/merge_requests/img/versions.png diff --git a/doc/user/project/merge_requests/img/versions_compare.png b/doc/user/project/merge_requests/img/versions_compare.png Binary files differindex f5bd85dc7c1..0957a0310ac 100644 --- a/doc/user/project/merge_requests/img/versions_compare.png +++ b/doc/user/project/merge_requests/img/versions_compare.png diff --git a/doc/user/project/merge_requests/img/versions_dropdown.png b/doc/user/project/merge_requests/img/versions_dropdown.png Binary files differindex cc70a5bf14b..831c92db2c0 100644 --- a/doc/user/project/merge_requests/img/versions_dropdown.png +++ b/doc/user/project/merge_requests/img/versions_dropdown.png diff --git a/doc/user/project/merge_requests/img/versions_system_note.png b/doc/user/project/merge_requests/img/versions_system_note.png Binary files differindex 90be6298d15..97d552692c9 100644 --- a/doc/user/project/merge_requests/img/versions_system_note.png +++ b/doc/user/project/merge_requests/img/versions_system_note.png diff --git a/doc/user/project/merge_requests/img/wip_blocked_accept_button.png b/doc/user/project/merge_requests/img/wip_blocked_accept_button.png Binary files differindex 0c492aca363..31f23be4d3d 100644 --- a/doc/user/project/merge_requests/img/wip_blocked_accept_button.png +++ b/doc/user/project/merge_requests/img/wip_blocked_accept_button.png diff --git a/doc/user/project/merge_requests/img/wip_mark_as_wip.png b/doc/user/project/merge_requests/img/wip_mark_as_wip.png Binary files differindex e405879b28a..2c2a263b316 100644 --- a/doc/user/project/merge_requests/img/wip_mark_as_wip.png +++ b/doc/user/project/merge_requests/img/wip_mark_as_wip.png diff --git a/doc/user/project/merge_requests/img/wip_unmark_as_wip.png b/doc/user/project/merge_requests/img/wip_unmark_as_wip.png Binary files differindex d7f8c419945..327ad9a8448 100644 --- a/doc/user/project/merge_requests/img/wip_unmark_as_wip.png +++ b/doc/user/project/merge_requests/img/wip_unmark_as_wip.png diff --git a/doc/user/project/milestones/img/milestones_new_group_milestone.png b/doc/user/project/milestones/img/milestones_new_group_milestone.png Binary files differindex b6defab101d..a517f0f7537 100644 --- a/doc/user/project/milestones/img/milestones_new_group_milestone.png +++ b/doc/user/project/milestones/img/milestones_new_group_milestone.png diff --git a/doc/user/project/milestones/img/milestones_new_project_milestone.png b/doc/user/project/milestones/img/milestones_new_project_milestone.png Binary files differindex 9aaff7dfef1..482c73f6568 100644 --- a/doc/user/project/milestones/img/milestones_new_project_milestone.png +++ b/doc/user/project/milestones/img/milestones_new_project_milestone.png diff --git a/doc/user/project/milestones/img/milestones_project_milestone_page.png b/doc/user/project/milestones/img/milestones_project_milestone_page.png Binary files differindex 9717075b8d0..c17bf350aeb 100644 --- a/doc/user/project/milestones/img/milestones_project_milestone_page.png +++ b/doc/user/project/milestones/img/milestones_project_milestone_page.png diff --git a/doc/user/project/milestones/img/milestones_promote_milestone.png b/doc/user/project/milestones/img/milestones_promote_milestone.png Binary files differindex 5e7f94c316f..2ef85c5951d 100644 --- a/doc/user/project/milestones/img/milestones_promote_milestone.png +++ b/doc/user/project/milestones/img/milestones_promote_milestone.png diff --git a/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png b/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png Binary files differindex fa72df66587..0150329d4b2 100644 --- a/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png +++ b/doc/user/project/pages/img/dns_add_new_a_record_example_updated_2018.png diff --git a/doc/user/project/pages/img/pages_create_project.png b/doc/user/project/pages/img/pages_create_project.png Binary files differindex be47f9d2a44..69e84b84984 100644 --- a/doc/user/project/pages/img/pages_create_project.png +++ b/doc/user/project/pages/img/pages_create_project.png diff --git a/doc/user/project/pages/img/pages_dns_details.png b/doc/user/project/pages/img/pages_dns_details.png Binary files differindex 274e98fde4d..3e57f43f7ba 100644 --- a/doc/user/project/pages/img/pages_dns_details.png +++ b/doc/user/project/pages/img/pages_dns_details.png diff --git a/doc/user/project/pages/img/pages_multiple_domains.png b/doc/user/project/pages/img/pages_multiple_domains.png Binary files differindex 6bc92db6b41..76c39101439 100644 --- a/doc/user/project/pages/img/pages_multiple_domains.png +++ b/doc/user/project/pages/img/pages_multiple_domains.png diff --git a/doc/user/project/pages/img/pages_remove.png b/doc/user/project/pages/img/pages_remove.png Binary files differindex b064310380e..10299880247 100644 --- a/doc/user/project/pages/img/pages_remove.png +++ b/doc/user/project/pages/img/pages_remove.png diff --git a/doc/user/project/pages/img/pages_upload_cert.png b/doc/user/project/pages/img/pages_upload_cert.png Binary files differindex dc431ea3fef..64e5f8eced1 100644 --- a/doc/user/project/pages/img/pages_upload_cert.png +++ b/doc/user/project/pages/img/pages_upload_cert.png diff --git a/doc/user/project/pages/img/verify_your_domain.png b/doc/user/project/pages/img/verify_your_domain.png Binary files differindex 89c69cac9a5..d870f9e6505 100644 --- a/doc/user/project/pages/img/verify_your_domain.png +++ b/doc/user/project/pages/img/verify_your_domain.png diff --git a/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png b/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png Binary files differindex 3ccce4f9bb4..983f903ca72 100644 --- a/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png +++ b/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png diff --git a/doc/user/project/pipelines/img/pipeline_schedule_play.png b/doc/user/project/pipelines/img/pipeline_schedule_play.png Binary files differindex f594ceee19d..ec6eb0d156b 100644 --- a/doc/user/project/pipelines/img/pipeline_schedule_play.png +++ b/doc/user/project/pipelines/img/pipeline_schedule_play.png diff --git a/doc/user/project/pipelines/img/pipeline_schedule_variables.png b/doc/user/project/pipelines/img/pipeline_schedule_variables.png Binary files differindex 47a0c6f3697..74692add93a 100644 --- a/doc/user/project/pipelines/img/pipeline_schedule_variables.png +++ b/doc/user/project/pipelines/img/pipeline_schedule_variables.png diff --git a/doc/user/project/pipelines/img/pipeline_schedules_list.png b/doc/user/project/pipelines/img/pipeline_schedules_list.png Binary files differindex 2ab2061db94..541fe4f9b1d 100644 --- a/doc/user/project/pipelines/img/pipeline_schedules_list.png +++ b/doc/user/project/pipelines/img/pipeline_schedules_list.png diff --git a/doc/user/project/pipelines/img/pipeline_schedules_new_form.png b/doc/user/project/pipelines/img/pipeline_schedules_new_form.png Binary files differindex 5a0e5965992..95203ec861b 100644 --- a/doc/user/project/pipelines/img/pipeline_schedules_new_form.png +++ b/doc/user/project/pipelines/img/pipeline_schedules_new_form.png diff --git a/doc/user/project/pipelines/img/pipeline_schedules_ownership.png b/doc/user/project/pipelines/img/pipeline_schedules_ownership.png Binary files differindex 31ed83abb4d..8fc5c5fbc82 100644 --- a/doc/user/project/pipelines/img/pipeline_schedules_ownership.png +++ b/doc/user/project/pipelines/img/pipeline_schedules_ownership.png diff --git a/doc/user/project/repository/branches/img/delete_merged_branches.png b/doc/user/project/repository/branches/img/delete_merged_branches.png Binary files differindex 1856a624f74..649a758b95f 100644 --- a/doc/user/project/repository/branches/img/delete_merged_branches.png +++ b/doc/user/project/repository/branches/img/delete_merged_branches.png diff --git a/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png b/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png Binary files differindex 8e26d98f1b0..6e2ff33eebb 100644 --- a/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png +++ b/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png diff --git a/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png b/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png Binary files differindex 5c14df36d73..ae0a8696c6c 100644 --- a/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png +++ b/doc/user/project/repository/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png diff --git a/doc/user/project/repository/gpg_signed_commits/img/project_signed_and_unsigned_commits.png b/doc/user/project/repository/gpg_signed_commits/img/project_signed_and_unsigned_commits.png Binary files differindex 088ecfa6d89..e1d44f15f3f 100644 --- a/doc/user/project/repository/gpg_signed_commits/img/project_signed_and_unsigned_commits.png +++ b/doc/user/project/repository/gpg_signed_commits/img/project_signed_and_unsigned_commits.png diff --git a/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_unverified_signature.png b/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_unverified_signature.png Binary files differindex 4e3392406b1..763a677f94a 100644 --- a/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_unverified_signature.png +++ b/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_unverified_signature.png diff --git a/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_verified_signature.png b/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_verified_signature.png Binary files differindex 766970dee81..1b6fa3fc2e2 100644 --- a/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_verified_signature.png +++ b/doc/user/project/repository/gpg_signed_commits/img/project_signed_commit_verified_signature.png diff --git a/doc/user/project/repository/img/compare_branches.png b/doc/user/project/repository/img/compare_branches.png Binary files differindex d7ab587f030..52d5c518c45 100644 --- a/doc/user/project/repository/img/compare_branches.png +++ b/doc/user/project/repository/img/compare_branches.png diff --git a/doc/user/project/repository/img/repository_languages.png b/doc/user/project/repository/img/repository_languages.png Binary files differindex d9fb1278e06..5977ad7faae 100644 --- a/doc/user/project/repository/img/repository_languages.png +++ b/doc/user/project/repository/img/repository_languages.png diff --git a/doc/user/project/repository/img/web_editor_new_branch_dropdown.png b/doc/user/project/repository/img/web_editor_new_branch_dropdown.png Binary files differindex 31edb6bde3a..a6edea1fcce 100644 --- a/doc/user/project/repository/img/web_editor_new_branch_dropdown.png +++ b/doc/user/project/repository/img/web_editor_new_branch_dropdown.png diff --git a/doc/user/project/repository/img/web_editor_new_branch_from_issue.png b/doc/user/project/repository/img/web_editor_new_branch_from_issue.png Binary files differindex 4729f5383c0..4e156b8adc8 100644 --- a/doc/user/project/repository/img/web_editor_new_branch_from_issue.png +++ b/doc/user/project/repository/img/web_editor_new_branch_from_issue.png diff --git a/doc/user/project/repository/img/web_editor_new_branch_page.png b/doc/user/project/repository/img/web_editor_new_branch_page.png Binary files differindex 8d82f981527..7bb8b9e29e3 100644 --- a/doc/user/project/repository/img/web_editor_new_branch_page.png +++ b/doc/user/project/repository/img/web_editor_new_branch_page.png diff --git a/doc/user/project/repository/img/web_editor_new_directory_dialog.png b/doc/user/project/repository/img/web_editor_new_directory_dialog.png Binary files differindex 1c9beff8849..590989c360e 100644 --- a/doc/user/project/repository/img/web_editor_new_directory_dialog.png +++ b/doc/user/project/repository/img/web_editor_new_directory_dialog.png diff --git a/doc/user/project/repository/img/web_editor_new_directory_dropdown.png b/doc/user/project/repository/img/web_editor_new_directory_dropdown.png Binary files differindex ede691f6f74..efa3087fc0c 100644 --- a/doc/user/project/repository/img/web_editor_new_directory_dropdown.png +++ b/doc/user/project/repository/img/web_editor_new_directory_dropdown.png diff --git a/doc/user/project/repository/img/web_editor_new_file_dropdown.png b/doc/user/project/repository/img/web_editor_new_file_dropdown.png Binary files differindex 13a4d721039..b40fb1ce58d 100644 --- a/doc/user/project/repository/img/web_editor_new_file_dropdown.png +++ b/doc/user/project/repository/img/web_editor_new_file_dropdown.png diff --git a/doc/user/project/repository/img/web_editor_new_push_widget.png b/doc/user/project/repository/img/web_editor_new_push_widget.png Binary files differindex 77756876d4f..8957b5d6a6b 100644 --- a/doc/user/project/repository/img/web_editor_new_push_widget.png +++ b/doc/user/project/repository/img/web_editor_new_push_widget.png diff --git a/doc/user/project/repository/img/web_editor_new_tag_dropdown.png b/doc/user/project/repository/img/web_editor_new_tag_dropdown.png Binary files differindex b52d5cabdf2..33e8ed891b5 100644 --- a/doc/user/project/repository/img/web_editor_new_tag_dropdown.png +++ b/doc/user/project/repository/img/web_editor_new_tag_dropdown.png diff --git a/doc/user/project/repository/img/web_editor_start_new_merge_request.png b/doc/user/project/repository/img/web_editor_start_new_merge_request.png Binary files differindex 384e8320f15..85f4769661a 100644 --- a/doc/user/project/repository/img/web_editor_start_new_merge_request.png +++ b/doc/user/project/repository/img/web_editor_start_new_merge_request.png diff --git a/doc/user/project/repository/img/web_editor_template_dropdown_buttons.png b/doc/user/project/repository/img/web_editor_template_dropdown_buttons.png Binary files differindex f21183125f6..4608843b1f4 100644 --- a/doc/user/project/repository/img/web_editor_template_dropdown_buttons.png +++ b/doc/user/project/repository/img/web_editor_template_dropdown_buttons.png diff --git a/doc/user/project/repository/img/web_editor_template_dropdown_first_file.png b/doc/user/project/repository/img/web_editor_template_dropdown_first_file.png Binary files differindex 7f31c2a8887..a4440ec3cc9 100644 --- a/doc/user/project/repository/img/web_editor_template_dropdown_first_file.png +++ b/doc/user/project/repository/img/web_editor_template_dropdown_first_file.png diff --git a/doc/user/project/repository/img/web_editor_upload_file_dialog.png b/doc/user/project/repository/img/web_editor_upload_file_dialog.png Binary files differindex 04e951406ad..c0e9a99aa61 100644 --- a/doc/user/project/repository/img/web_editor_upload_file_dialog.png +++ b/doc/user/project/repository/img/web_editor_upload_file_dialog.png diff --git a/doc/user/project/repository/img/web_editor_upload_file_dropdown.png b/doc/user/project/repository/img/web_editor_upload_file_dropdown.png Binary files differindex b8c766d4b99..c80a9ae4b3d 100644 --- a/doc/user/project/repository/img/web_editor_upload_file_dropdown.png +++ b/doc/user/project/repository/img/web_editor_upload_file_dropdown.png diff --git a/doc/user/project/settings/img/import_export_download_export.png b/doc/user/project/settings/img/import_export_download_export.png Binary files differindex 4945590e3e8..668254073e8 100644 --- a/doc/user/project/settings/img/import_export_download_export.png +++ b/doc/user/project/settings/img/import_export_download_export.png diff --git a/doc/user/project/settings/img/import_export_export_button.png b/doc/user/project/settings/img/import_export_export_button.png Binary files differindex eef79821f8b..7f21bb2335b 100644 --- a/doc/user/project/settings/img/import_export_export_button.png +++ b/doc/user/project/settings/img/import_export_export_button.png diff --git a/doc/user/project/settings/img/import_export_new_project.png b/doc/user/project/settings/img/import_export_new_project.png Binary files differindex 9dd509dc4a0..b335700c5be 100644 --- a/doc/user/project/settings/img/import_export_new_project.png +++ b/doc/user/project/settings/img/import_export_new_project.png diff --git a/doc/user/project/settings/img/import_export_select_file.png b/doc/user/project/settings/img/import_export_select_file.png Binary files differindex fb831dca32b..e1e5e031d81 100644 --- a/doc/user/project/settings/img/import_export_select_file.png +++ b/doc/user/project/settings/img/import_export_select_file.png diff --git a/doc/user/project/settings/img/settings_edit_button.png b/doc/user/project/settings/img/settings_edit_button.png Binary files differindex 9f3a8330e3a..32bcda03c7e 100644 --- a/doc/user/project/settings/img/settings_edit_button.png +++ b/doc/user/project/settings/img/settings_edit_button.png diff --git a/doc/user/project/settings/img/sharing_and_permissions_settings.png b/doc/user/project/settings/img/sharing_and_permissions_settings.png Binary files differindex 0f9cf9512af..f5e3e32f95c 100644 --- a/doc/user/project/settings/img/sharing_and_permissions_settings.png +++ b/doc/user/project/settings/img/sharing_and_permissions_settings.png diff --git a/doc/user/project/web_ide/img/open_web_ide.png b/doc/user/project/web_ide/img/open_web_ide.png Binary files differindex d1192daf506..02a5a564472 100644 --- a/doc/user/project/web_ide/img/open_web_ide.png +++ b/doc/user/project/web_ide/img/open_web_ide.png diff --git a/doc/user/project/wiki/img/wiki_create_home_page.png b/doc/user/project/wiki/img/wiki_create_home_page.png Binary files differindex f50f564034c..658af33d76e 100644 --- a/doc/user/project/wiki/img/wiki_create_home_page.png +++ b/doc/user/project/wiki/img/wiki_create_home_page.png diff --git a/doc/user/project/wiki/img/wiki_create_new_page.png b/doc/user/project/wiki/img/wiki_create_new_page.png Binary files differindex c19124a8923..8954ec0d3a8 100644 --- a/doc/user/project/wiki/img/wiki_create_new_page.png +++ b/doc/user/project/wiki/img/wiki_create_new_page.png diff --git a/doc/user/project/wiki/img/wiki_create_new_page_modal.png b/doc/user/project/wiki/img/wiki_create_new_page_modal.png Binary files differindex ece437967dc..b800508901b 100644 --- a/doc/user/project/wiki/img/wiki_create_new_page_modal.png +++ b/doc/user/project/wiki/img/wiki_create_new_page_modal.png diff --git a/doc/user/project/wiki/img/wiki_move_page_1.png b/doc/user/project/wiki/img/wiki_move_page_1.png Binary files differindex 0331c9d3a5c..189fcc9a845 100644 --- a/doc/user/project/wiki/img/wiki_move_page_1.png +++ b/doc/user/project/wiki/img/wiki_move_page_1.png diff --git a/doc/user/project/wiki/img/wiki_move_page_2.png b/doc/user/project/wiki/img/wiki_move_page_2.png Binary files differindex a8e0c055051..63e6ddb29c1 100644 --- a/doc/user/project/wiki/img/wiki_move_page_2.png +++ b/doc/user/project/wiki/img/wiki_move_page_2.png diff --git a/doc/user/project/wiki/img/wiki_page_history.png b/doc/user/project/wiki/img/wiki_page_history.png Binary files differindex 0e6af1b468d..5a1ae295ed2 100644 --- a/doc/user/project/wiki/img/wiki_page_history.png +++ b/doc/user/project/wiki/img/wiki_page_history.png diff --git a/doc/user/project/wiki/img/wiki_sidebar.png b/doc/user/project/wiki/img/wiki_sidebar.png Binary files differindex 59814e2a06e..ff39c861a73 100644 --- a/doc/user/project/wiki/img/wiki_sidebar.png +++ b/doc/user/project/wiki/img/wiki_sidebar.png diff --git a/doc/user/search/img/issue_search_by_term.png b/doc/user/search/img/issue_search_by_term.png Binary files differindex 3cefa3adb8b..64450c6a891 100644 --- a/doc/user/search/img/issue_search_by_term.png +++ b/doc/user/search/img/issue_search_by_term.png diff --git a/doc/user/search/img/issue_search_filter.png b/doc/user/search/img/issue_search_filter.png Binary files differindex f357abd6bac..d4de3ff7656 100644 --- a/doc/user/search/img/issue_search_filter.png +++ b/doc/user/search/img/issue_search_filter.png diff --git a/doc/user/search/img/issues_mrs_shortcut.png b/doc/user/search/img/issues_mrs_shortcut.png Binary files differindex cf43df98aa0..2fe1350c806 100644 --- a/doc/user/search/img/issues_mrs_shortcut.png +++ b/doc/user/search/img/issues_mrs_shortcut.png diff --git a/doc/user/search/img/project_search.png b/doc/user/search/img/project_search.png Binary files differindex 0b76d7d6038..b2525b2c771 100644 --- a/doc/user/search/img/project_search.png +++ b/doc/user/search/img/project_search.png diff --git a/doc/workflow/ci_mr.png b/doc/workflow/ci_mr.png Binary files differindex 77423c68190..85a609cb814 100644 --- a/doc/workflow/ci_mr.png +++ b/doc/workflow/ci_mr.png diff --git a/doc/workflow/environment_branches.png b/doc/workflow/environment_branches.png Binary files differindex 0941a4cad9c..0aff33c6bb8 100644 --- a/doc/workflow/environment_branches.png +++ b/doc/workflow/environment_branches.png diff --git a/doc/workflow/forking/branch_select.png b/doc/workflow/forking/branch_select.png Binary files differindex 3e82afca75b..77236137190 100644 --- a/doc/workflow/forking/branch_select.png +++ b/doc/workflow/forking/branch_select.png diff --git a/doc/workflow/forking/merge_request.png b/doc/workflow/forking/merge_request.png Binary files differindex 294775e1fdd..407ddfb4799 100644 --- a/doc/workflow/forking/merge_request.png +++ b/doc/workflow/forking/merge_request.png diff --git a/doc/workflow/git_pull.png b/doc/workflow/git_pull.png Binary files differindex 2dd06b56c56..0e56e59471c 100644 --- a/doc/workflow/git_pull.png +++ b/doc/workflow/git_pull.png diff --git a/doc/workflow/gitlab_flow.png b/doc/workflow/gitlab_flow.png Binary files differindex c3562cc69a8..a6f3c947843 100644 --- a/doc/workflow/gitlab_flow.png +++ b/doc/workflow/gitlab_flow.png diff --git a/doc/workflow/good_commit.png b/doc/workflow/good_commit.png Binary files differindex c3664aa97f2..ceb0d4b1691 100644 --- a/doc/workflow/good_commit.png +++ b/doc/workflow/good_commit.png diff --git a/doc/workflow/img/file_finder_find_button.png b/doc/workflow/img/file_finder_find_button.png Binary files differindex 23139cc00c5..0c2d7d7bc73 100644 --- a/doc/workflow/img/file_finder_find_button.png +++ b/doc/workflow/img/file_finder_find_button.png diff --git a/doc/workflow/img/forking_workflow_fork_button.png b/doc/workflow/img/forking_workflow_fork_button.png Binary files differindex 29854e6c516..941d5363c35 100644 --- a/doc/workflow/img/forking_workflow_fork_button.png +++ b/doc/workflow/img/forking_workflow_fork_button.png diff --git a/doc/workflow/img/forking_workflow_path_taken_error.png b/doc/workflow/img/forking_workflow_path_taken_error.png Binary files differindex 9365fd13200..df938da5677 100644 --- a/doc/workflow/img/forking_workflow_path_taken_error.png +++ b/doc/workflow/img/forking_workflow_path_taken_error.png diff --git a/doc/workflow/img/notification_group_settings.png b/doc/workflow/img/notification_group_settings.png Binary files differindex fc096f46901..ed5e9459216 100644 --- a/doc/workflow/img/notification_group_settings.png +++ b/doc/workflow/img/notification_group_settings.png diff --git a/doc/workflow/img/notification_project_settings.png b/doc/workflow/img/notification_project_settings.png Binary files differindex 006432f65c9..e2db2037d94 100644 --- a/doc/workflow/img/notification_project_settings.png +++ b/doc/workflow/img/notification_project_settings.png diff --git a/doc/workflow/img/todo_list_item.png b/doc/workflow/img/todo_list_item.png Binary files differindex 076069b651e..91bbf9e5373 100644 --- a/doc/workflow/img/todo_list_item.png +++ b/doc/workflow/img/todo_list_item.png diff --git a/doc/workflow/img/todos_add_todo_sidebar.png b/doc/workflow/img/todos_add_todo_sidebar.png Binary files differindex 3fa37067d1e..aefec7a2d9c 100644 --- a/doc/workflow/img/todos_add_todo_sidebar.png +++ b/doc/workflow/img/todos_add_todo_sidebar.png diff --git a/doc/workflow/img/todos_mark_done_sidebar.png b/doc/workflow/img/todos_mark_done_sidebar.png Binary files differindex a8e756a71db..2badd880b40 100644 --- a/doc/workflow/img/todos_mark_done_sidebar.png +++ b/doc/workflow/img/todos_mark_done_sidebar.png diff --git a/doc/workflow/merge_request.png b/doc/workflow/merge_request.png Binary files differindex 08dfc7f2468..010e95983fc 100644 --- a/doc/workflow/merge_request.png +++ b/doc/workflow/merge_request.png diff --git a/doc/workflow/messy_flow.png b/doc/workflow/messy_flow.png Binary files differindex 7e72e2a3be6..4fa22d2bb5d 100644 --- a/doc/workflow/messy_flow.png +++ b/doc/workflow/messy_flow.png diff --git a/doc/workflow/mr_inline_comments.png b/doc/workflow/mr_inline_comments.png Binary files differindex 6a2e66a01ba..a18801f56e4 100644 --- a/doc/workflow/mr_inline_comments.png +++ b/doc/workflow/mr_inline_comments.png diff --git a/doc/workflow/production_branch.png b/doc/workflow/production_branch.png Binary files differindex 648d5d5c92e..c132d51bfb6 100644 --- a/doc/workflow/production_branch.png +++ b/doc/workflow/production_branch.png diff --git a/doc/workflow/rebase.png b/doc/workflow/rebase.png Binary files differindex 8b9bb61a5cc..fe865177ba8 100644 --- a/doc/workflow/rebase.png +++ b/doc/workflow/rebase.png diff --git a/doc/workflow/release_branches.png b/doc/workflow/release_branches.png Binary files differindex 5194d75a667..0a7f61d0248 100644 --- a/doc/workflow/release_branches.png +++ b/doc/workflow/release_branches.png diff --git a/doc/workflow/releases/new_tag.png b/doc/workflow/releases/new_tag.png Binary files differindex 97519e5808f..6137ad2ee56 100644 --- a/doc/workflow/releases/new_tag.png +++ b/doc/workflow/releases/new_tag.png diff --git a/doc/workflow/repository_mirroring/repository_mirroring_detect_host_keys.png b/doc/workflow/repository_mirroring/repository_mirroring_detect_host_keys.png Binary files differindex 333648942f8..2377a4a6516 100644 --- a/doc/workflow/repository_mirroring/repository_mirroring_detect_host_keys.png +++ b/doc/workflow/repository_mirroring/repository_mirroring_detect_host_keys.png diff --git a/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch_push.png b/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch_push.png Binary files differindex 038b05cb31d..786bd23eee6 100644 --- a/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch_push.png +++ b/doc/workflow/repository_mirroring/repository_mirroring_diverged_branch_push.png diff --git a/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_main.png b/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_main.png Binary files differindex 99d429a1802..d8af5ce129e 100644 --- a/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_main.png +++ b/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_main.png diff --git a/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_settings.png b/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_settings.png Binary files differindex 0ab07afa3cc..a10102e97ac 100644 --- a/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_settings.png +++ b/doc/workflow/repository_mirroring/repository_mirroring_hard_failed_settings.png diff --git a/doc/workflow/repository_mirroring/repository_mirroring_pull_advanced_host_keys.png b/doc/workflow/repository_mirroring/repository_mirroring_pull_advanced_host_keys.png Binary files differindex 5da5a7436bb..1f1b3e1d5fb 100644 --- a/doc/workflow/repository_mirroring/repository_mirroring_pull_advanced_host_keys.png +++ b/doc/workflow/repository_mirroring/repository_mirroring_pull_advanced_host_keys.png diff --git a/doc/workflow/repository_mirroring/repository_mirroring_pull_settings.png b/doc/workflow/repository_mirroring/repository_mirroring_pull_settings.png Binary files differindex 4b9085302a1..b8dfddb3d02 100644 --- a/doc/workflow/repository_mirroring/repository_mirroring_pull_settings.png +++ b/doc/workflow/repository_mirroring/repository_mirroring_pull_settings.png diff --git a/doc/workflow/repository_mirroring/repository_mirroring_pull_settings_for_ssh.png b/doc/workflow/repository_mirroring/repository_mirroring_pull_settings_for_ssh.png Binary files differindex 8c2efdafa43..8f1de1d3003 100644 --- a/doc/workflow/repository_mirroring/repository_mirroring_pull_settings_for_ssh.png +++ b/doc/workflow/repository_mirroring/repository_mirroring_pull_settings_for_ssh.png diff --git a/doc/workflow/repository_mirroring/repository_mirroring_ssh_host_keys_verified.png b/doc/workflow/repository_mirroring/repository_mirroring_ssh_host_keys_verified.png Binary files differindex 93f3a532a0e..930d10a0822 100644 --- a/doc/workflow/repository_mirroring/repository_mirroring_ssh_host_keys_verified.png +++ b/doc/workflow/repository_mirroring/repository_mirroring_ssh_host_keys_verified.png diff --git a/doc/workflow/repository_mirroring/repository_mirroring_ssh_public_key_authentication.png b/doc/workflow/repository_mirroring/repository_mirroring_ssh_public_key_authentication.png Binary files differindex 6997ad511d9..adc1eedac44 100644 --- a/doc/workflow/repository_mirroring/repository_mirroring_ssh_public_key_authentication.png +++ b/doc/workflow/repository_mirroring/repository_mirroring_ssh_public_key_authentication.png diff --git a/doc/workflow/time-tracking/time-tracking-example.png b/doc/workflow/time-tracking/time-tracking-example.png Binary files differindex bbcabb602d6..a96e4da7f74 100644 --- a/doc/workflow/time-tracking/time-tracking-example.png +++ b/doc/workflow/time-tracking/time-tracking-example.png diff --git a/doc/workflow/time-tracking/time-tracking-sidebar.png b/doc/workflow/time-tracking/time-tracking-sidebar.png Binary files differindex d1ff5571f95..22124afed6f 100644 --- a/doc/workflow/time-tracking/time-tracking-sidebar.png +++ b/doc/workflow/time-tracking/time-tracking-sidebar.png diff --git a/lib/api/api.rb b/lib/api/api.rb index e2ad3c5f4e3..c000666d992 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -99,12 +99,13 @@ module API mount ::API::Features mount ::API::Files mount ::API::GroupBoards - mount ::API::Groups mount ::API::GroupMilestones + mount ::API::Groups + mount ::API::GroupVariables mount ::API::Internal mount ::API::Issues - mount ::API::Jobs mount ::API::JobArtifacts + mount ::API::Jobs mount ::API::Keys mount ::API::Labels mount ::API::Lint @@ -122,11 +123,12 @@ module API mount ::API::ProjectExport mount ::API::ProjectImport mount ::API::ProjectHooks - mount ::API::Projects mount ::API::ProjectMilestones + mount ::API::Projects mount ::API::ProjectSnapshots mount ::API::ProjectSnippets mount ::API::ProtectedBranches + mount ::API::ProtectedTags mount ::API::Repositories mount ::API::Runner mount ::API::Runners @@ -143,7 +145,6 @@ module API mount ::API::Triggers mount ::API::Users mount ::API::Variables - mount ::API::GroupVariables mount ::API::Version mount ::API::Wikis diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb index c3d93996816..bde4b3ff4f6 100644 --- a/lib/api/award_emoji.rb +++ b/lib/api/award_emoji.rb @@ -100,7 +100,7 @@ module API end def can_award_awardable? - awardable.user_can_award?(current_user, params[:name]) + awardable.user_can_award?(current_user) end def awardable diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 06262f0f991..95b25d7351a 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -429,6 +429,11 @@ module API expose :merge_access_levels, using: Entities::ProtectedRefAccess end + class ProtectedTag < Grape::Entity + expose :name + expose :create_access_levels, using: Entities::ProtectedRefAccess + end + class Milestone < Grape::Entity expose :id, :iid expose :project_id, if: -> (entity, options) { entity&.project_id } diff --git a/lib/api/group_milestones.rb b/lib/api/group_milestones.rb index 93fa0b95857..4b4352c2b27 100644 --- a/lib/api/group_milestones.rb +++ b/lib/api/group_milestones.rb @@ -41,7 +41,7 @@ module API use :optional_params end post ":id/milestones" do - authorize! :admin_milestones, user_group + authorize! :admin_milestone, user_group create_milestone_for(user_group) end @@ -53,11 +53,21 @@ module API use :update_params end put ":id/milestones/:milestone_id" do - authorize! :admin_milestones, user_group + authorize! :admin_milestone, user_group update_milestone_for(user_group) end + desc 'Remove a project milestone' + delete ":id/milestones/:milestone_id" do + authorize! :admin_milestone, user_group + + milestone = user_group.milestones.find(params[:milestone_id]) + Milestones::DestroyService.new(user_group, current_user).execute(milestone) + + status(204) + end + desc 'Get all issues for a single group milestone' do success Entities::IssueBasic end diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb index e2984b08eca..7b1f5c2584b 100644 --- a/lib/api/helpers/notes_helpers.rb +++ b/lib/api/helpers/notes_helpers.rb @@ -92,10 +92,7 @@ module API parent = noteable_parent(noteable) - if opts[:created_at] - opts.delete(:created_at) unless - current_user.admin? || parent.owned_by?(current_user) - end + opts.delete(:created_at) unless current_user.can?(:set_note_created_at, policy_object) opts[:updated_at] = opts[:created_at] if opts[:created_at] diff --git a/lib/api/issues.rb b/lib/api/issues.rb index bda05d1795b..cedfd2fbaa0 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -172,11 +172,8 @@ module API authorize! :create_issue, user_project - # Setting created_at time or iid only allowed for admins and project owners - unless current_user.admin? || user_project.owner == current_user - params.delete(:created_at) - params.delete(:iid) - end + params.delete(:created_at) unless current_user.can?(:set_issue_created_at, user_project) + params.delete(:iid) unless current_user.can?(:set_issue_iid, user_project) issue_params = declared_params(include_missing: false) @@ -216,8 +213,8 @@ module API issue = user_project.issues.find_by!(iid: params.delete(:issue_iid)) authorize! :update_issue, issue - # Setting created_at time only allowed for admins and project owners - unless current_user.admin? || user_project.owner == current_user + # Setting created_at time only allowed for admins and project/group owners + unless current_user.admin? || user_project.owner == current_user || current_user.owned_groups.include?(user_project.owner) params.delete(:updated_at) end diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb index 306dc0e63d7..72cf32d7717 100644 --- a/lib/api/project_milestones.rb +++ b/lib/api/project_milestones.rb @@ -64,7 +64,8 @@ module API delete ":id/milestones/:milestone_id" do authorize! :admin_milestone, user_project - user_project.milestones.find(params[:milestone_id]).destroy + milestone = user_project.milestones.find(params[:milestone_id]) + Milestones::DestroyService.new(user_project, current_user).execute(milestone) status(204) end diff --git a/lib/api/protected_tags.rb b/lib/api/protected_tags.rb new file mode 100644 index 00000000000..bf0a7184e1c --- /dev/null +++ b/lib/api/protected_tags.rb @@ -0,0 +1,79 @@ +module API + class ProtectedTags < Grape::API + include PaginationParams + + TAG_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(name: API::NO_SLASH_URL_PART_REGEX) + + before { authorize_admin_project } + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do + desc "Get a project's protected tags" do + detail 'This feature was introduced in GitLab 11.3.' + success Entities::ProtectedTag + end + params do + use :pagination + end + get ':id/protected_tags' do + protected_tags = user_project.protected_tags.preload(:create_access_levels) + + present paginate(protected_tags), with: Entities::ProtectedTag, project: user_project + end + + desc 'Get a single protected tag' do + detail 'This feature was introduced in GitLab 11.3.' + success Entities::ProtectedTag + end + params do + requires :name, type: String, desc: 'The name of the tag or wildcard' + end + get ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do + protected_tag = user_project.protected_tags.find_by!(name: params[:name]) + + present protected_tag, with: Entities::ProtectedTag, project: user_project + end + + desc 'Protect a single tag or wildcard' do + detail 'This feature was introduced in GitLab 11.3.' + success Entities::ProtectedTag + end + params do + requires :name, type: String, desc: 'The name of the protected tag' + optional :create_access_level, type: Integer, default: Gitlab::Access::MAINTAINER, + values: ProtectedRefAccess::ALLOWED_ACCESS_LEVELS, + desc: 'Access levels allowed to create (defaults: `40`, maintainer access level)' + end + post ':id/protected_tags' do + protected_tags_params = { + name: params[:name], + create_access_levels_attributes: [{ access_level: params[:create_access_level] }] + } + + protected_tag = ::ProtectedTags::CreateService.new(user_project, + current_user, + protected_tags_params).execute + + if protected_tag.persisted? + present protected_tag, with: Entities::ProtectedTag, project: user_project + else + render_api_error!(protected_tag.errors.full_messages, 422) + end + end + + desc 'Unprotect a single tag' do + detail 'This feature was introduced in GitLab 11.3.' + end + params do + requires :name, type: String, desc: 'The name of the protected tag' + end + delete ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do + protected_tag = user_project.protected_tags.find_by!(name: params[:name]) + + destroy_conditionally!(protected_tag) + end + end + end +end diff --git a/lib/banzai/reference_parser/merge_request_parser.rb b/lib/banzai/reference_parser/merge_request_parser.rb index a370ff5b5b3..9e5d55f72bc 100644 --- a/lib/banzai/reference_parser/merge_request_parser.rb +++ b/lib/banzai/reference_parser/merge_request_parser.rb @@ -14,11 +14,12 @@ module Banzai # Eager loading these ensures we don't end up running dozens of # queries in this process. target_project: [ - { namespace: :owner }, + { namespace: [:owner, :route] }, { group: [:owners, :group_members] }, :invited_groups, :project_members, - :project_feature + :project_feature, + :route ] }), self.class.data_attribute diff --git a/lib/feature.rb b/lib/feature.rb index 09c5ef3ad94..24dbcb32fc0 100644 --- a/lib/feature.rb +++ b/lib/feature.rb @@ -47,7 +47,8 @@ class Feature end def disabled?(key, thing = nil) - !enabled?(key, thing) + # we need to make different method calls to make it easy to mock / define expectations in test mode + thing.nil? ? !enabled?(key) : !enabled?(key, thing) end def enable(key, thing = true) diff --git a/lib/gitlab/auth/ldap/access.rb b/lib/gitlab/auth/ldap/access.rb index 865185eb5db..eeab7791643 100644 --- a/lib/gitlab/auth/ldap/access.rb +++ b/lib/gitlab/auth/ldap/access.rb @@ -19,8 +19,10 @@ module Gitlab # Whether user is allowed, or not, we should update # permissions to keep things clean if access.allowed? - access.update_user - Users::UpdateService.new(user, user: user, last_credential_check_at: Time.now).execute + unless Gitlab::Database.read_only? + access.update_user + Users::UpdateService.new(user, user: user, last_credential_check_at: Time.now).execute + end true else @@ -60,6 +62,12 @@ module Gitlab false end + def update_user + # no-op in CE + end + + private + def adapter @adapter ||= Gitlab::Auth::LDAP::Adapter.new(provider) end @@ -68,16 +76,16 @@ module Gitlab Gitlab::Auth::LDAP::Config.new(provider) end - def find_ldap_user - Gitlab::Auth::LDAP::Person.find_by_dn(ldap_identity.extern_uid, adapter) - end - def ldap_user return unless provider @ldap_user ||= find_ldap_user end + def find_ldap_user + Gitlab::Auth::LDAP::Person.find_by_dn(ldap_identity.extern_uid, adapter) + end + def block_user(user, reason) user.ldap_block @@ -102,10 +110,6 @@ module Gitlab "unblocking Gitlab user \"#{user.name}\" (#{user.email})" ) end - - def update_user - # no-op in CE - end end end end diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb index e73743944a9..26da9d09ccc 100644 --- a/lib/gitlab/auth/o_auth/provider.rb +++ b/lib/gitlab/auth/o_auth/provider.rb @@ -29,6 +29,7 @@ module Gitlab def self.enabled?(name) return true if name == 'database' + return true if self.ldap_provider?(name) && providers.include?(name.to_sym) Gitlab::Auth.omniauth_enabled? && providers.include?(name.to_sym) end diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 268d21a77d1..b591d94668f 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -1,7 +1,10 @@ +# frozen_string_literal: true + module Gitlab module BitbucketServerImport class Importer include Gitlab::ShellAdapter + attr_reader :recover_missing_commits attr_reader :project, :project_key, :repository_slug, :client, :errors, :users @@ -175,21 +178,18 @@ module Gitlab description = '' description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email) description += pull_request.description if pull_request.description - - source_branch_sha = pull_request.source_branch_sha - target_branch_sha = pull_request.target_branch_sha author_id = gitlab_user_id(pull_request.author_email) attributes = { iid: pull_request.iid, title: pull_request.title, description: description, - source_project: project, + source_project_id: project.id, source_branch: Gitlab::Git.ref_name(pull_request.source_branch_name), - source_branch_sha: source_branch_sha, - target_project: project, + source_branch_sha: pull_request.source_branch_sha, + target_project_id: project.id, target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name), - target_branch_sha: target_branch_sha, + target_branch_sha: pull_request.target_branch_sha, state: pull_request.state, author_id: author_id, assignee_id: nil, @@ -197,7 +197,9 @@ module Gitlab updated_at: pull_request.updated_at } - merge_request = project.merge_requests.create!(attributes) + creator = Gitlab::Import::MergeRequestCreator.new(project) + merge_request = creator.execute(attributes) + import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? end diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb index 703f0b9217b..508b4814631 100644 --- a/lib/gitlab/ci/status/build/failed.rb +++ b/lib/gitlab/ci/status/build/failed.rb @@ -32,7 +32,7 @@ module Gitlab end def description - "<br> (#{failure_reason_message})" + "- (#{failure_reason_message})" end def failure_reason_message diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb index 5c1baa19b66..1f012043e56 100644 --- a/lib/gitlab/diff/highlight.rb +++ b/lib/gitlab/diff/highlight.rb @@ -37,7 +37,7 @@ module Gitlab end end - diff_line.text = rich_line + diff_line.rich_text = rich_line diff_line end diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb index 1faf7770634..1ab6df0b6ae 100644 --- a/lib/gitlab/diff/line.rb +++ b/lib/gitlab/diff/line.rb @@ -1,16 +1,17 @@ module Gitlab module Diff class Line - SERIALIZE_KEYS = %i(line_code text type index old_pos new_pos).freeze + SERIALIZE_KEYS = %i(line_code rich_text text type index old_pos new_pos).freeze attr_reader :line_code, :type, :index, :old_pos, :new_pos attr_writer :rich_text attr_accessor :text - def initialize(text, type, index, old_pos, new_pos, parent_file: nil, line_code: nil) + def initialize(text, type, index, old_pos, new_pos, parent_file: nil, line_code: nil, rich_text: nil) @text, @type, @index = text, type, index @old_pos, @new_pos = old_pos, new_pos @parent_file = parent_file + @rich_text = rich_text # When line code is not provided from cache store we build it # using the parent_file(Diff::File or Conflict::File). @@ -18,7 +19,7 @@ module Gitlab end def self.init_from_hash(hash) - new(hash[:text], hash[:type], hash[:index], hash[:old_pos], hash[:new_pos], line_code: hash[:line_code]) + new(hash[:text], hash[:type], hash[:index], hash[:old_pos], hash[:new_pos], line_code: hash[:line_code], rich_text: hash[:rich_text]) end def to_hash @@ -85,7 +86,7 @@ module Gitlab old_line: old_line, new_line: new_line, text: text, - rich_text: rich_text || text, + rich_text: rich_text || CGI.escapeHTML(text), meta_data: meta_positions } end diff --git a/lib/gitlab/github_import.rb b/lib/gitlab/github_import.rb index 65b5e30c70f..d40b06f969f 100644 --- a/lib/gitlab/github_import.rb +++ b/lib/gitlab/github_import.rb @@ -10,24 +10,6 @@ module Gitlab Client.new(token_to_use, parallel: parallel) end - # Inserts a raw row and returns the ID of the inserted row. - # - # attributes - The attributes/columns to set. - # relation - An ActiveRecord::Relation to use for finding the ID of the row - # when using MySQL. - def self.insert_and_return_id(attributes, relation) - # We use bulk_insert here so we can bypass any queries executed by - # callbacks or validation rules, as doing this wouldn't scale when - # importing very large projects. - result = Gitlab::Database - .bulk_insert(relation.table_name, [attributes], return_ids: true) - - # MySQL doesn't support returning the IDs of a bulk insert in a way that - # is not a pain, so in this case we'll issue an extra query instead. - result.first || - relation.where(iid: attributes[:iid]).limit(1).pluck(:id).first - end - # Returns the ID of the ghost user. def self.ghost_user_id key = 'github-import/ghost-user-id' diff --git a/lib/gitlab/github_import/importer/issue_importer.rb b/lib/gitlab/github_import/importer/issue_importer.rb index cb4d7a6a0b6..4226eee85cc 100644 --- a/lib/gitlab/github_import/importer/issue_importer.rb +++ b/lib/gitlab/github_import/importer/issue_importer.rb @@ -4,6 +4,8 @@ module Gitlab module GithubImport module Importer class IssueImporter + include Gitlab::Import::DatabaseHelpers + attr_reader :project, :issue, :client, :user_finder, :milestone_finder, :issuable_finder @@ -55,7 +57,7 @@ module Gitlab updated_at: issue.updated_at } - GithubImport.insert_and_return_id(attributes, project.issues).tap do |id| + insert_and_return_id(attributes, project.issues).tap do |id| # We use .insert_and_return_id which effectively disables all callbacks. # Trigger iid logic here to make sure we track internal id values consistently. project.issues.find(id).ensure_project_iid! diff --git a/lib/gitlab/github_import/importer/pull_request_importer.rb b/lib/gitlab/github_import/importer/pull_request_importer.rb index ed17aa54373..ae7c4cf1b38 100644 --- a/lib/gitlab/github_import/importer/pull_request_importer.rb +++ b/lib/gitlab/github_import/importer/pull_request_importer.rb @@ -4,6 +4,8 @@ module Gitlab module GithubImport module Importer class PullRequestImporter + include Gitlab::Import::MergeRequestHelpers + attr_reader :pull_request, :project, :client, :user_finder, :milestone_finder, :issuable_finder @@ -44,81 +46,27 @@ module Gitlab description = MarkdownText .format(pull_request.description, pull_request.author, author_found) - # This work must be wrapped in a transaction as otherwise we can leave - # behind incomplete data in the event of an error. This can then lead - # to duplicate key errors when jobs are retried. - MergeRequest.transaction do - attributes = { - iid: pull_request.iid, - title: pull_request.truncated_title, - description: description, - source_project_id: project.id, - target_project_id: project.id, - source_branch: pull_request.formatted_source_branch, - target_branch: pull_request.target_branch, - state: pull_request.state, - milestone_id: milestone_finder.id_for(pull_request), - author_id: author_id, - assignee_id: user_finder.assignee_id_for(pull_request), - created_at: pull_request.created_at, - updated_at: pull_request.updated_at - } - - # When creating merge requests there are a lot of hooks that may - # run, for many different reasons. Many of these hooks (e.g. the - # ones used for rendering Markdown) are completely unnecessary and - # may even lead to transaction timeouts. - # - # To ensure importing pull requests has a minimal impact and can - # complete in a reasonable time we bypass all the hooks by inserting - # the row and then retrieving it. We then only perform the - # additional work that is strictly necessary. - merge_request_id = GithubImport - .insert_and_return_id(attributes, project.merge_requests) - - merge_request = project.merge_requests.find(merge_request_id) - - # We use .insert_and_return_id which effectively disables all callbacks. - # Trigger iid logic here to make sure we track internal id values consistently. - merge_request.ensure_target_project_iid! + attributes = { + iid: pull_request.iid, + title: pull_request.truncated_title, + description: description, + source_project_id: project.id, + target_project_id: project.id, + source_branch: pull_request.formatted_source_branch, + target_branch: pull_request.target_branch, + state: pull_request.state, + milestone_id: milestone_finder.id_for(pull_request), + author_id: author_id, + assignee_id: user_finder.assignee_id_for(pull_request), + created_at: pull_request.created_at, + updated_at: pull_request.updated_at + } - [merge_request, false] - end - rescue ActiveRecord::InvalidForeignKey - # It's possible the project has been deleted since scheduling this - # job. In this case we'll just skip creating the merge request. - [] - rescue ActiveRecord::RecordNotUnique - # It's possible we previously created the MR, but failed when updating - # the Git data. In this case we'll just continue working on the - # existing row. - [project.merge_requests.find_by(iid: pull_request.iid), true] + create_merge_request_without_hooks(project, attributes, pull_request.iid) end - def insert_git_data(merge_request, already_exists = false) - # These fields are set so we can create the correct merge request - # diffs. - merge_request.source_branch_sha = pull_request.source_branch_sha - merge_request.target_branch_sha = pull_request.target_branch_sha - - merge_request.keep_around_commit - - # MR diffs normally use an "after_save" hook to pull data from Git. - # All of this happens in the transaction started by calling - # create/save/etc. This in turn can lead to these transactions being - # held open for much longer than necessary. To work around this we - # first save the diff, then populate it. - diff = - if already_exists - merge_request.merge_request_diffs.take || - merge_request.merge_request_diffs.build - else - merge_request.merge_request_diffs.build - end - - diff.importing = true - diff.save - diff.save_git_content + def insert_git_data(merge_request, already_exists) + insert_or_replace_git_data(merge_request, pull_request.source_branch_sha, pull_request.target_branch_sha, already_exists) end end end diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb index b8213929c6a..7346eab9e76 100644 --- a/lib/gitlab/i18n.rb +++ b/lib/gitlab/i18n.rb @@ -5,6 +5,7 @@ module Gitlab AVAILABLE_LANGUAGES = { 'en' => 'English', 'es' => 'Español', + 'gl_ES' => 'Galego', 'de' => 'Deutsch', 'fr' => 'Français', 'pt_BR' => 'Português (Brasil)', diff --git a/lib/gitlab/import/database_helpers.rb b/lib/gitlab/import/database_helpers.rb new file mode 100644 index 00000000000..80857061933 --- /dev/null +++ b/lib/gitlab/import/database_helpers.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Gitlab + module Import + module DatabaseHelpers + # Inserts a raw row and returns the ID of the inserted row. + # + # attributes - The attributes/columns to set. + # relation - An ActiveRecord::Relation to use for finding the ID of the row + # when using MySQL. + def insert_and_return_id(attributes, relation) + # We use bulk_insert here so we can bypass any queries executed by + # callbacks or validation rules, as doing this wouldn't scale when + # importing very large projects. + result = Gitlab::Database + .bulk_insert(relation.table_name, [attributes], return_ids: true) + + # MySQL doesn't support returning the IDs of a bulk insert in a way that + # is not a pain, so in this case we'll issue an extra query instead. + result.first || + relation.where(iid: attributes[:iid]).limit(1).pluck(:id).first + end + end + end +end diff --git a/lib/gitlab/import/merge_request_creator.rb b/lib/gitlab/import/merge_request_creator.rb new file mode 100644 index 00000000000..a01951b0762 --- /dev/null +++ b/lib/gitlab/import/merge_request_creator.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# This module is designed for importers that need to create many merge +# requests quickly. When creating merge requests there are a lot of hooks +# that may run, for many different reasons. Many of these hooks (e.g. the ones +# used for rendering Markdown) are completely unnecessary and may even lead to +# transaction timeouts. +# +# To ensure importing merge requests requests has a minimal impact and can +# complete in a reasonable time we bypass all the hooks by inserting the row +# and then retrieving it. We then only perform the additional work that is +# strictly necessary. +module Gitlab + module Import + class MergeRequestCreator + include ::Gitlab::Import::DatabaseHelpers + include ::Gitlab::Import::MergeRequestHelpers + + attr_accessor :project + + def initialize(project) + @project = project + end + + def execute(attributes) + source_branch_sha = attributes.delete(:source_branch_sha) + target_branch_sha = attributes.delete(:target_branch_sha) + iid = attributes[:iid] + + merge_request, already_exists = create_merge_request_without_hooks(project, attributes, iid) + + if merge_request + insert_or_replace_git_data(merge_request, source_branch_sha, target_branch_sha, already_exists) + end + + merge_request + end + end + end +end diff --git a/lib/gitlab/import/merge_request_helpers.rb b/lib/gitlab/import/merge_request_helpers.rb new file mode 100644 index 00000000000..8ba70700dc1 --- /dev/null +++ b/lib/gitlab/import/merge_request_helpers.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module Gitlab + module Import + module MergeRequestHelpers + include DatabaseHelpers + + def create_merge_request_without_hooks(project, attributes, iid) + # This work must be wrapped in a transaction as otherwise we can leave + # behind incomplete data in the event of an error. This can then lead + # to duplicate key errors when jobs are retried. + MergeRequest.transaction do + # When creating merge requests there are a lot of hooks that may + # run, for many different reasons. Many of these hooks (e.g. the + # ones used for rendering Markdown) are completely unnecessary and + # may even lead to transaction timeouts. + # + # To ensure importing pull requests has a minimal impact and can + # complete in a reasonable time we bypass all the hooks by inserting + # the row and then retrieving it. We then only perform the + # additional work that is strictly necessary. + merge_request_id = insert_and_return_id(attributes, project.merge_requests) + + merge_request = project.merge_requests.find(merge_request_id) + + # We use .insert_and_return_id which effectively disables all callbacks. + # Trigger iid logic here to make sure we track internal id values consistently. + merge_request.ensure_target_project_iid! + + [merge_request, false] + end + rescue ActiveRecord::InvalidForeignKey + # It's possible the project has been deleted since scheduling this + # job. In this case we'll just skip creating the merge request. + [] + rescue ActiveRecord::RecordNotUnique + # It's possible we previously created the MR, but failed when updating + # the Git data. In this case we'll just continue working on the + # existing row. + [project.merge_requests.find_by(iid: iid), true] + end + + def insert_or_replace_git_data(merge_request, source_branch_sha, target_branch_sha, already_exists = false) + # These fields are set so we can create the correct merge request + # diffs. + merge_request.source_branch_sha = source_branch_sha + merge_request.target_branch_sha = target_branch_sha + + merge_request.keep_around_commit + + # MR diffs normally use an "after_save" hook to pull data from Git. + # All of this happens in the transaction started by calling + # create/save/etc. This in turn can lead to these transactions being + # held open for much longer than necessary. To work around this we + # first save the diff, then populate it. + diff = + if already_exists + merge_request.merge_request_diffs.take || + merge_request.merge_request_diffs.build + else + merge_request.merge_request_diffs.build + end + + diff.importing = true + diff.save + diff.save_git_content + end + end + end +end diff --git a/lib/gitlab/import_export/uploads_manager.rb b/lib/gitlab/import_export/uploads_manager.rb index 07875ebb56a..e0d4235e65b 100644 --- a/lib/gitlab/import_export/uploads_manager.rb +++ b/lib/gitlab/import_export/uploads_manager.rb @@ -13,13 +13,11 @@ module Gitlab end def save - copy_files(@from, uploads_export_path) if File.directory?(@from) - if File.file?(@from) && @relative_export_path == 'avatar' copy_files(@from, File.join(uploads_export_path, @project.avatar.filename)) end - copy_from_object_storage + copy_project_uploads true rescue => e @@ -48,14 +46,19 @@ module Gitlab UploadService.new(@project, File.open(upload, 'r'), FileUploader, uploader_context).execute end - def copy_from_object_storage - return unless Gitlab::ImportExport.object_storage? - + def copy_project_uploads each_uploader do |uploader| next unless uploader.file - next if uploader.upload.local? # Already copied, using the old method - download_and_copy(uploader) + if uploader.upload.local? + next unless uploader.upload.exist? + + copy_files(uploader.absolute_path, File.join(uploads_export_path, uploader.upload.path)) + else + next unless Gitlab::ImportExport.object_storage? + + download_and_copy(uploader) + end end end diff --git a/lib/gitlab/middleware/multipart.rb b/lib/gitlab/middleware/multipart.rb index 18f91db98fc..3d588918adf 100644 --- a/lib/gitlab/middleware/multipart.rb +++ b/lib/gitlab/middleware/multipart.rb @@ -82,9 +82,13 @@ module Gitlab end def open_file(params, key) - ::UploadedFile.from_params( - params, key, - [FileUploader.root, Gitlab.config.uploads.storage_path]) + allowed_paths = [ + FileUploader.root, + Gitlab.config.uploads.storage_path, + File.join(Rails.root, 'public/uploads/tmp') + ] + + ::UploadedFile.from_params(params, key, allowed_paths) end end diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb index 38be75b7482..3b483f27e70 100644 --- a/lib/gitlab/url_blocker.rb +++ b/lib/gitlab/url_blocker.rb @@ -31,6 +31,7 @@ module Gitlab validate_localhost!(addrs_info) unless allow_localhost validate_local_network!(addrs_info) unless allow_local_network + validate_link_local!(addrs_info) unless allow_local_network true end @@ -89,6 +90,13 @@ module Gitlab raise BlockedUrlError, "Requests to the local network are not allowed" end + def validate_link_local!(addrs_info) + netmask = IPAddr.new('169.254.0.0/16') + return unless addrs_info.any? { |addr| addr.ipv6_linklocal? || netmask.include?(addr.ip_address) } + + raise BlockedUrlError, "Requests to the link local network are not allowed" + end + def internal?(uri) internal_web?(uri) || internal_shell?(uri) end diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index 22c9638ecc0..7797bd5fab2 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -34,6 +34,7 @@ module Gitlab def system_usage_data { counts: { + assignee_lists: List.assignee.count, boards: Board.count, ci_builds: ::Ci::Build.count, ci_internal_pipelines: ::Ci::Pipeline.internal.count, @@ -61,9 +62,11 @@ module Gitlab groups: Group.count, issues: Issue.count, keys: Key.count, + label_lists: List.label.count, labels: Label.count, lfs_objects: LfsObject.count, merge_requests: MergeRequest.count, + milestone_lists: List.milestone.count, milestones: Milestone.count, notes: Note.count, pages_domains: PagesDomain.count, diff --git a/lib/tasks/gettext.rake b/lib/tasks/gettext.rake index f431352b61e..a497d26312e 100644 --- a/lib/tasks/gettext.rake +++ b/lib/tasks/gettext.rake @@ -82,7 +82,7 @@ namespace :gettext do # `gettext:find` writes touches to temp files to `stderr` which would cause # `static-analysis` to report failures. We can ignore these. - silence_stream($stderr) do + silence_sdterr do Rake::Task['gettext:find'].invoke end @@ -118,4 +118,15 @@ namespace :gettext do end end end + + def silence_sdterr(&block) + old_stderr = $stderr.dup + $stderr.reopen(File::NULL) + $stderr.sync = true + + yield + ensure + $stderr.reopen(old_stderr) + old_stderr.close + end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f348f66ae17..ce5d82d479b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2391,6 +2391,18 @@ msgstr "" msgid "Enter in your Bitbucket Server URL and personal access token below" msgstr "" +msgid "Enter the issue description" +msgstr "" + +msgid "Enter the issue title" +msgstr "" + +msgid "Enter the merge request description" +msgstr "" + +msgid "Enter the merge request title" +msgstr "" + msgid "Environments" msgstr "" @@ -3620,10 +3632,10 @@ msgstr "" msgid "Milestones" msgstr "" -msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} from this project and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered." +msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered." msgstr "" -msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} from this project. %{milestoneTitle} is not currently used in any issues or merge requests." +msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle}. This milestone is not currently used in any issues or merge requests." msgstr "" msgid "Milestones|Delete milestone" @@ -5050,6 +5062,9 @@ msgstr "" msgid "Settings" msgstr "" +msgid "Setup a %{type} Runner manually" +msgstr "" + msgid "Setup a specific Runner automatically" msgstr "" @@ -5477,6 +5492,9 @@ msgstr "" msgid "The collection of events added to the data gathered for that stage." msgstr "" +msgid "The deployment of this job to %{environmentLink} did not succeed." +msgstr "" + msgid "The fork relationship has been removed." msgstr "" @@ -5552,6 +5570,9 @@ msgstr "" msgid "The time taken by each data entry gathered by that stage." msgstr "" +msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination." +msgstr "" + msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side." msgstr "" @@ -5657,6 +5678,18 @@ msgstr "" msgid "This job has not started yet" msgstr "" +msgid "This job is an out-of-date deployment to %{environmentLink}." +msgstr "" + +msgid "This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}." +msgstr "" + +msgid "This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}." +msgstr "" + +msgid "This job is creating a deployment to %{environmentLink}." +msgstr "" + msgid "This job is in pending state and is waiting to be picked by a runner" msgstr "" @@ -5666,6 +5699,9 @@ msgstr "" msgid "This job is stuck, because you don't have any active runners that can run this job." msgstr "" +msgid "This job is the most recent deployment to %{link}." +msgstr "" + msgid "This job requires a manual action" msgstr "" @@ -6706,6 +6742,9 @@ msgstr "" msgid "mrWidget|The source branch has been removed" msgstr "" +msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch" +msgstr "" + msgid "mrWidget|The source branch is being removed" msgstr "" @@ -83,6 +83,7 @@ module QA # Test scenario entrypoints. # module Test + autoload :Instance, 'qa/scenario/test/instance' module Instance autoload :All, 'qa/scenario/test/instance/all' autoload :Smoke, 'qa/scenario/test/instance/smoke' diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb index 36567927194..1a410a0f8a5 100644 --- a/qa/qa/page/project/import/github.rb +++ b/qa/qa/page/project/import/github.rb @@ -14,7 +14,7 @@ module QA element :project_import_row, 'data: { qa: { repo_path: repo.full_name } }' element :project_namespace_select element :project_namespace_field, 'select_tag :namespace_id' - element :project_path_field, 'text_field_tag :path, repo.name' + element :project_path_field, 'text_field_tag :path, sanitize_project_name(repo.name)' element :import_button, "_('Import')" end diff --git a/qa/qa/scenario/template.rb b/qa/qa/scenario/template.rb index 66eb86f25c8..a87d925ce32 100644 --- a/qa/qa/scenario/template.rb +++ b/qa/qa/scenario/template.rb @@ -21,14 +21,18 @@ module QA def perform(address, *rspec_options) Runtime::Scenario.define(:gitlab_address, address) + ## + # Perform before hooks, which are different for CE and EE + # + Runtime::Release.perform_before_hooks + Specs::Runner.perform do |specs| specs.tty = true - specs.tags = self.class.focus specs.options = if rspec_options.any? rspec_options else - ::File.expand_path('../specs/features', __dir__) + ['--tag', self.class.focus.join(','), '--', ::File.expand_path('../specs/features', __dir__)] end end end diff --git a/qa/qa/scenario/test/instance.rb b/qa/qa/scenario/test/instance.rb new file mode 100644 index 00000000000..a2d503cc015 --- /dev/null +++ b/qa/qa/scenario/test/instance.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module QA + module Scenario + module Test + # This class exists for back-compatibility so that gitlab-qa can continue + # to call Test::Instance instead of Test::Instance::All until at least + # the current latest GitLab version has the Test::Instance::All class. + # As of Aug, 22nd 2018. Only GitLab >= 11.3 has this class. + module Instance + include Bootable + + def self.perform(*args) + self.tap do |scenario| + yield scenario if block_given? + break scenario.do_perform(*args) + end + end + + def self.do_perform(address, *rspec_options) + Runtime::Scenario.define(:gitlab_address, address) + + ## + # Perform before hooks, which are different for CE and EE + # + Runtime::Release.perform_before_hooks + + Specs::Runner.perform do |specs| + specs.tty = true + specs.options = + if rspec_options.any? + rspec_options + else + ['--', ::File.expand_path('../../specs/features', __dir__)] + end + end + end + end + end + end +end diff --git a/qa/qa/specs/features/api/1_manage/.gitkeep b/qa/qa/specs/features/api/1_manage/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/qa/qa/specs/features/api/1_manage/.gitkeep diff --git a/qa/qa/specs/features/api/users_spec.rb b/qa/qa/specs/features/api/1_manage/users_spec.rb index 3d25cca1e59..3e3c9e859aa 100644 --- a/qa/qa/specs/features/api/users_spec.rb +++ b/qa/qa/specs/features/api/1_manage/users_spec.rb @@ -1,19 +1,21 @@ +# frozen_string_literal: true + module QA - describe 'API users' do - before(:context) do - @api_client = Runtime::API::Client.new(:gitlab) - end + context :manage do + describe 'Users API' do + before(:context) do + @api_client = Runtime::API::Client.new(:gitlab) + end - context 'when authenticated' do let(:request) { Runtime::API::Request.new(@api_client, '/users') } - it 'get list of users' do + it 'GET /users' do get request.url expect_status(200) end - it 'submit request with a valid user name' do + it 'GET /users/:username with a valid username' do get request.url, { params: { username: Runtime::User.username } } expect_status(200) @@ -22,20 +24,12 @@ module QA ) end - it 'submit request with an invalid user name' do + it 'GET /users/:username with an invalid username' do get request.url, { params: { username: SecureRandom.hex(10) } } expect_status(200) expect(json_body).to eq([]) end end - - it 'submit request with an invalid token' do - request = Runtime::API::Request.new(@api_client, '/users', private_token: 'invalid') - - get request.url - - expect_status(401) - end end end diff --git a/qa/qa/specs/features/api/2_plan/.gitkeep b/qa/qa/specs/features/api/2_plan/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/qa/qa/specs/features/api/2_plan/.gitkeep diff --git a/qa/qa/specs/features/api/basics_spec.rb b/qa/qa/specs/features/api/3_create/repository/files_spec.rb index bc0b5ebfe10..bc0b5ebfe10 100644 --- a/qa/qa/specs/features/api/basics_spec.rb +++ b/qa/qa/specs/features/api/3_create/repository/files_spec.rb diff --git a/qa/qa/specs/features/api/4_verify/.gitkeep b/qa/qa/specs/features/api/4_verify/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/qa/qa/specs/features/api/4_verify/.gitkeep diff --git a/qa/qa/specs/features/api/5_package/.gitkeep b/qa/qa/specs/features/api/5_package/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/qa/qa/specs/features/api/5_package/.gitkeep diff --git a/qa/qa/specs/features/api/6_release/.gitkeep b/qa/qa/specs/features/api/6_release/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/qa/qa/specs/features/api/6_release/.gitkeep diff --git a/qa/qa/specs/features/api/7_configure/.gitkeep b/qa/qa/specs/features/api/7_configure/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/qa/qa/specs/features/api/7_configure/.gitkeep diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb new file mode 100644 index 00000000000..1c7da930567 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb @@ -0,0 +1,17 @@ +module QA + context :manage, :smoke do + describe 'basic user login' do + it 'user logs in using basic credentials' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + # TODO, since `Signed in successfully` message was removed + # this is the only way to tell if user is signed in correctly. + # + Page::Menu::Main.perform do |menu| + expect(menu).to have_personal_area + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb new file mode 100644 index 00000000000..c9958917be9 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module QA + context :manage, :orchestrated, :ldap do + describe 'LDAP login' do + before do + Runtime::Env.user_type = 'ldap' + end + + it 'user logs into GitLab using LDAP credentials' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + # TODO, since `Signed in successfully` message was removed + # this is the only way to tell if user is signed in correctly. + # + Page::Menu::Main.perform do |menu| + expect(menu).to have_personal_area + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb new file mode 100644 index 00000000000..6eda2c750d4 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_into_mattermost_via_gitlab_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module QA + context :manage, :orchestrated, :mattermost do + describe 'Mattermost login' do + it 'user logs into Mattermost using GitLab OAuth' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) do + Page::Main::Login.act { sign_in_using_credentials } + + Runtime::Browser.visit(:mattermost, Page::Mattermost::Login) do + Page::Mattermost::Login.act { sign_in_using_oauth } + + Page::Mattermost::Main.perform do |page| + expect(page).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/) + end + end + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb new file mode 100644 index 00000000000..bb1f3ab26d1 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module QA + context :manage, :smoke do + describe 'Project creation' do + it 'user creates a new project' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + created_project = Factory::Resource::Project.fabricate! do |project| + project.name = 'awesome-project' + project.description = 'create awesome project test' + end + + expect(created_project.name).to match /^awesome-project-\h{16}$/ + + expect(page).to have_content( + /Project \S?awesome-project\S+ was successfully created/ + ) + + expect(page).to have_content('create awesome project test') + expect(page).to have_content('The repository for this project is empty') + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb new file mode 100644 index 00000000000..2ef8de61441 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +module QA + context :manage, :orchestrated, :github do + describe 'Project import from GitHub' do + let(:imported_project) do + Factory::Resource::ProjectImportedFromGithub.fabricate! do |project| + project.name = 'imported-project' + project.personal_access_token = Runtime::Env.github_access_token + project.github_repository_path = 'gitlab-qa/test-project' + end + end + + after do + # We need to delete the imported project because it's impossible to import + # the same GitHub project twice for a given user. + api_client = Runtime::API::Client.new(:gitlab) + delete_project_request = Runtime::API::Request.new(api_client, "/projects/#{CGI.escape("#{Runtime::Namespace.path}/#{imported_project.name}")}") + delete delete_project_request.url + + expect_status(202) + end + + it 'user imports a GitHub repo' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + imported_project # import the project + + Page::Menu::Main.act { go_to_projects } + Page::Dashboard::Projects.perform do |dashboard| + dashboard.go_to_project(imported_project.name) + end + + Page::Project::Show.act { wait_for_import } + + verify_repository_import + verify_issues_import + verify_merge_requests_import + verify_labels_import + verify_milestones_import + verify_wiki_import + end + + def verify_repository_import + expect(page).to have_content('This test project is used for automated GitHub import by GitLab QA.') + expect(page).to have_content(imported_project.name) + end + + def verify_issues_import + Page::Menu::Side.act { click_issues } + expect(page).to have_content('This is a sample issue') + + click_link 'This is a sample issue' + + expect(page).to have_content('We should populate this project with issues, pull requests and wiki pages.') + + # Comments + expect(page).to have_content('This is a comment from @rymai.') + + Page::Issuable::Sidebar.perform do |issuable| + expect(issuable).to have_label('enhancement') + expect(issuable).to have_label('help wanted') + expect(issuable).to have_label('good first issue') + end + end + + def verify_merge_requests_import + Page::Menu::Side.act { click_merge_requests } + expect(page).to have_content('Improve README.md') + + click_link 'Improve README.md' + + expect(page).to have_content('This improves the README file a bit.') + + # Review comment are not supported yet + expect(page).not_to have_content('Really nice change.') + + # Comments + expect(page).to have_content('Nice work! This is a comment from @rymai.') + + # Diff comments + expect(page).to have_content('[Review comment] I like that!') + expect(page).to have_content('[Review comment] Nice blank line.') + expect(page).to have_content('[Single diff comment] Much better without this line!') + + Page::Issuable::Sidebar.perform do |issuable| + expect(issuable).to have_label('bug') + expect(issuable).to have_label('enhancement') + end + end + + def verify_labels_import + # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19228 + # to build upon it. + end + + def verify_milestones_import + # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18727 + # to build upon it. + end + + def verify_wiki_import + Page::Menu::Side.act { click_wiki } + + expect(page).to have_content('Welcome to the test-project wiki!') + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb new file mode 100644 index 00000000000..34bb6f1c197 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module QA + context :manage do + describe 'Project activity' do + it 'user creates an event in the activity page upon Git push' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + Factory::Repository::ProjectPush.fabricate! do |push| + push.file_name = 'README.md' + push.file_content = '# This is a test project' + push.commit_message = 'Add README.md' + end + + Page::Menu::Side.act { go_to_activity } + + Page::Project::Activity.act { go_to_push_events } + + expect(page).to have_content('pushed new branch master') + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb new file mode 100644 index 00000000000..dd1be935220 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module QA + context :plan, :smoke do + describe 'Issue creation' do + let(:issue_title) { 'issue title' } + + it 'user creates an issue' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + Factory::Resource::Issue.fabricate! do |issue| + issue.title = issue_title + end + + Page::Menu::Side.act { click_issues } + + expect(page).to have_content(issue_title) + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb new file mode 100644 index 00000000000..bcf55a02a61 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'Merge request creation' do + it 'user creates a new merge request' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + current_project = Factory::Resource::Project.fabricate! do |project| + project.name = 'project-with-merge-request-and-milestone' + end + + current_milestone = Factory::Resource::ProjectMilestone.fabricate! do |milestone| + milestone.title = 'unique-milestone' + milestone.project = current_project + end + + Factory::Resource::MergeRequest.fabricate! do |merge_request| + merge_request.title = 'This is a merge request with a milestone' + merge_request.description = 'Great feature with milestone' + merge_request.project = current_project + merge_request.milestone = current_milestone + end + + expect(page).to have_content('This is a merge request with a milestone') + expect(page).to have_content('Great feature with milestone') + expect(page).to have_content(/Opened [\w\s]+ ago/) + + Page::Issuable::Sidebar.perform do |sidebar| + expect(sidebar).to have_milestone(current_milestone.title) + end + end + end + end + + describe 'creates a merge request', :smoke do + it 'user creates a new merge request' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + current_project = Factory::Resource::Project.fabricate! do |project| + project.name = 'project-with-merge-request' + end + + Factory::Resource::MergeRequest.fabricate! do |merge_request| + merge_request.title = 'This is a merge request' + merge_request.description = 'Great feature' + merge_request.project = current_project + end + + expect(page).to have_content('This is a merge request') + expect(page).to have_content('Great feature') + expect(page).to have_content(/Opened [\w\s]+ ago/) + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb new file mode 100644 index 00000000000..407a15800ab --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'Merge request creation from fork' do + it 'user forks a project, submits a merge request and maintainer merges it' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + merge_request = Factory::Resource::MergeRequestFromFork.fabricate! do |merge_request| + merge_request.fork_branch = 'feature-branch' + end + + Page::Menu::Main.perform { |main| main.sign_out } + Page::Main::Login.perform { |login| login.sign_in_using_credentials } + + merge_request.visit! + + Page::MergeRequest::Show.perform { |show| show.merge! } + + expect(page).to have_content('The changes were merged') + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb new file mode 100644 index 00000000000..ddcbc94b1b1 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'Merge request rebasing' do + it 'user rebases source branch of merge request' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + project = Factory::Resource::Project.fabricate! do |project| + project.name = "only-fast-forward" + end + + Page::Menu::Side.act { go_to_settings } + Page::Project::Settings::MergeRequest.act { enable_ff_only } + + merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request| + merge_request.project = project + merge_request.title = 'Needs rebasing' + end + + Factory::Repository::ProjectPush.fabricate! do |push| + push.project = project + push.file_name = "other.txt" + push.file_content = "New file added!" + push.branch_name = "master" + push.new_branch = false + end + + merge_request.visit! + + Page::MergeRequest::Show.perform do |merge_request| + expect(merge_request).to have_content('Needs rebasing') + expect(merge_request).not_to be_fast_forward_possible + expect(merge_request).not_to have_merge_button + + merge_request.rebase! + + expect(merge_request).to have_merge_button + expect(merge_request.fast_forward_possible?).to be_truthy + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb new file mode 100644 index 00000000000..b5b8855a35d --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'Merge request squashing' do + it 'user squashes commits while merging' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + project = Factory::Resource::Project.fabricate! do |project| + project.name = "squash-before-merge" + end + + merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request| + merge_request.project = project + merge_request.title = 'Squashing commits' + end + + Factory::Repository::ProjectPush.fabricate! do |push| + push.project = project + push.commit_message = 'to be squashed' + push.branch_name = merge_request.source_branch + push.new_branch = false + push.file_name = 'other.txt' + push.file_content = "Test with unicode characters ❤✓€❄" + end + + merge_request.visit! + + expect(page).to have_text('to be squashed') + + Page::MergeRequest::Show.perform do |merge_request_page| + merge_request_page.mark_to_squash + merge_request_page.merge! + + merge_request.project.visit! + + Git::Repository.perform do |repository| + repository.uri = Page::Project::Show.act do + choose_repository_clone_http + repository_location.uri + end + + repository.use_default_credentials + + repository.act { clone } + + expect(repository.commits.size).to eq 3 + end + end + end + end + end +end diff --git a/qa/qa/specs/features/repository/clone_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb index 70881f0f8d1..0dcdc6639d1 100644 --- a/qa/qa/specs/features/repository/clone_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module QA - describe 'clone code from the repository', :ldap do - context 'with regular account over http' do + context :create do + describe 'Git clone over HTTP', :ldap do let(:location) do Page::Project::Show.act do choose_repository_clone_http diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb new file mode 100644 index 00000000000..f18655442c1 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module QA + context :create, :core do + describe 'Files management' do + it 'user creates, edits and deletes a file via the Web' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + # Create + file_name = 'QA Test - File name' + file_content = 'QA Test - File content' + commit_message_for_create = 'QA Test - Create new file' + + Factory::Resource::File.fabricate! do |file| + file.name = file_name + file.content = file_content + file.commit_message = commit_message_for_create + end + + expect(page).to have_content('The file has been successfully created.') + expect(page).to have_content(file_name) + expect(page).to have_content(file_content) + expect(page).to have_content(commit_message_for_create) + + # Edit + updated_file_content = 'QA Test - Updated file content' + commit_message_for_update = 'QA Test - Update file' + + Page::File::Show.act { click_edit } + + Page::File::Form.act do + remove_content + add_content(updated_file_content) + add_commit_message(commit_message_for_update) + commit_changes + end + + expect(page).to have_content('Your changes have been successfully committed.') + expect(page).to have_content(updated_file_content) + expect(page).to have_content(commit_message_for_update) + + # Delete + commit_message_for_delete = 'QA Test - Delete file' + + Page::File::Show.act do + click_delete + add_commit_message(commit_message_for_delete) + click_delete_file + end + + expect(page).to have_content('The file has been successfully deleted.') + expect(page).to have_content(commit_message_for_delete) + expect(page).to have_no_content(file_name) + end + end + end +end diff --git a/qa/qa/specs/features/repository/push_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb index 99d4c16c7c7..bf32569b6cb 100644 --- a/qa/qa/specs/features/repository/push_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module QA - describe 'push code to repository', :ldap do - context 'with regular account over http' do + context :create do + describe 'Git push over HTTP', :ldap do it 'user pushes code to the repository' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb new file mode 100644 index 00000000000..b2da685c477 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'Protected branch support', :ldap do + let(:branch_name) { 'protected-branch' } + let(:commit_message) { 'Protected push commit message' } + let(:project) do + Factory::Resource::Project.fabricate! do |resource| + resource.name = 'protected-branch-project' + end + end + + before do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + end + + after do + # We need to clear localStorage because we're using it for the dropdown, + # and capybara doesn't do this for us. + # https://github.com/teamcapybara/capybara/issues/1702 + Capybara.execute_script 'localStorage.clear()' + end + + context 'when developers and maintainers are allowed to push to a protected branch' do + it 'user with push rights successfully pushes to the protected branch' do + create_protected_branch(allow_to_push: true) + + push = push_new_file(branch_name) + + expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/) + end + end + + context 'when developers and maintainers are not allowed to push to a protected branch' do + it 'user without push rights fails to push to the protected branch' do + create_protected_branch(allow_to_push: false) + + push = push_new_file(branch_name) + + expect(push.output) + .to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/) + expect(push.output) + .to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/) + end + end + + def create_protected_branch(allow_to_push:) + Factory::Resource::Branch.fabricate! do |resource| + resource.branch_name = branch_name + resource.project = project + resource.allow_to_push = allow_to_push + resource.protected = true + end + end + + def push_new_file(branch) + Factory::Repository::ProjectPush.fabricate! do |resource| + resource.project = project + resource.file_name = 'new_file.md' + resource.file_content = '# This is a new file' + resource.commit_message = 'Add new_file.md' + resource.branch_name = branch_name + resource.new_branch = false + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb new file mode 100644 index 00000000000..8009b9e8609 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/wiki/create_edit_clone_push_wiki_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'Wiki management' do + def login + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + end + + def validate_content(content) + expect(page).to have_content('Wiki was successfully updated') + expect(page).to have_content(/#{content}/) + end + + before do + login + end + + it 'user creates, edits, clones, and pushes to the wiki' do + wiki = Factory::Resource::Wiki.fabricate! do |resource| + resource.title = 'Home' + resource.content = '# My First Wiki Content' + resource.message = 'Update home' + end + + validate_content('My First Wiki Content') + + Page::Project::Wiki::Edit.act { go_to_edit_page } + Page::Project::Wiki::New.perform do |page| + page.set_content("My Second Wiki Content") + page.save_changes + end + + validate_content('My Second Wiki Content') + + Factory::Repository::WikiPush.fabricate! do |push| + push.wiki = wiki + push.file_name = 'Home.md' + push.file_content = '# My Third Wiki Content' + push.commit_message = 'Update Home.md' + end + Page::Menu::Side.act { click_wiki } + + expect(page).to have_content('My Third Wiki Content') + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb new file mode 100644 index 00000000000..cdfe9b90e15 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module QA + context :verify, :orchestrated, :docker do + describe 'Pipeline creation and processing' do + let(:executor) { "qa-runner-#{Time.now.to_i}" } + + after do + Service::Runner.new(executor).remove! + end + + it 'users creates a pipeline which gets processed' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + project = Factory::Resource::Project.fabricate! do |project| + project.name = 'project-with-pipelines' + project.description = 'Project with CI/CD Pipelines.' + end + + Factory::Resource::Runner.fabricate! do |runner| + runner.project = project + runner.name = executor + runner.tags = %w[qa test] + end + + Factory::Repository::ProjectPush.fabricate! do |push| + push.project = project + push.file_name = '.gitlab-ci.yml' + push.commit_message = 'Add .gitlab-ci.yml' + push.file_content = <<~EOF + test-success: + tags: + - qa + - test + script: echo 'OK' + + test-failure: + tags: + - qa + - test + script: + - echo 'FAILURE' + - exit 1 + + test-tags: + tags: + - qa + - docker + script: echo 'NOOP' + + test-artifacts: + tags: + - qa + - test + script: mkdir my-artifacts; echo "CONTENTS" > my-artifacts/artifact.txt + artifacts: + paths: + - my-artifacts/ + EOF + end + + Page::Project::Show.act { wait_for_push } + + expect(page).to have_content('Add .gitlab-ci.yml') + + Page::Menu::Side.act { click_ci_cd_pipelines } + + expect(page).to have_content('All 1') + expect(page).to have_content('Add .gitlab-ci.yml') + + puts 'Waiting for the runner to process the pipeline' + sleep 15 # Runner should process all jobs within 15 seconds. + + Page::Project::Pipeline::Index.act { go_to_latest_pipeline } + + Page::Project::Pipeline::Show.perform do |pipeline| + expect(pipeline).to be_running + expect(pipeline).to have_build('test-success', status: :success) + expect(pipeline).to have_build('test-failure', status: :failed) + expect(pipeline).to have_build('test-tags', status: :pending) + expect(pipeline).to have_build('test-artifacts', status: :success) + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb new file mode 100644 index 00000000000..8d83a20f5bf --- /dev/null +++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module QA + context :verify, :docker do + describe 'Runner registration' do + let(:executor) { "qa-runner-#{Time.now.to_i}" } + + after do + Service::Runner.new(executor).remove! + end + + it 'user registers a new specific runner' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + Factory::Resource::Runner.fabricate! do |runner| + runner.name = executor + end + + Page::Project::Settings::CICD.perform do |settings| + sleep 5 # Runner should register within 5 seconds + settings.refresh + + settings.expand_runners_settings do |page| + expect(page).to have_content(executor) + expect(page).to have_online_runner + end + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/4_verify/secret_variable/add_secret_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/secret_variable/add_secret_variable_spec.rb new file mode 100644 index 00000000000..08a87df5837 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/4_verify/secret_variable/add_secret_variable_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module QA + context :verify do + describe 'Secret variable support' do + it 'user adds a secret variable' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + Factory::Resource::SecretVariable.fabricate! do |resource| + resource.key = 'VARIABLE_KEY' + resource.value = 'some secret variable' + end + + Page::Project::Settings::CICD.perform do |settings| + settings.expand_secret_variables do |page| + expect(page).to have_field(with: 'VARIABLE_KEY') + expect(page).not_to have_field(with: 'some secret variable') + + page.reveal_variables + + expect(page).to have_field(with: 'some secret variable') + end + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/5_package/.gitkeep b/qa/qa/specs/features/browser_ui/5_package/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/qa/qa/specs/features/browser_ui/5_package/.gitkeep diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb new file mode 100644 index 00000000000..17dfa887434 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module QA + context :release do + describe 'Deploy key creation' do + it 'user adds a deploy key' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + key = Runtime::Key::RSA.new + deploy_key_title = 'deploy key title' + deploy_key_value = key.public_key + + deploy_key = Factory::Resource::DeployKey.fabricate! do |resource| + resource.title = deploy_key_title + resource.key = deploy_key_value + end + + expect(deploy_key.fingerprint).to eq(key.fingerprint) + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb new file mode 100644 index 00000000000..8352d13b06d --- /dev/null +++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'digest/sha1' + +module QA + context :release, :docker do + describe 'Git clone using a deploy key' do + def login + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + end + + before(:all) do + login + + @runner_name = "qa-runner-#{Time.now.to_i}" + + @project = Factory::Resource::Project.fabricate! do |resource| + resource.name = 'deploy-key-clone-project' + end + + @repository_location = @project.repository_ssh_location + + Factory::Resource::Runner.fabricate! do |resource| + resource.project = @project + resource.name = @runner_name + resource.tags = %w[qa docker] + resource.image = 'gitlab/gitlab-runner:ubuntu' + end + + Page::Menu::Main.act { sign_out } + end + + after(:all) do + Service::Runner.new(@runner_name).remove! + end + + keys = [ + [Runtime::Key::RSA, 8192], + [Runtime::Key::ECDSA, 521], + [Runtime::Key::ED25519] + ] + + keys.each do |(key_class, bits)| + it "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do + key = key_class.new(*bits) + + login + + Factory::Resource::DeployKey.fabricate! do |resource| + resource.project = @project + resource.title = "deploy key #{key.name}(#{key.bits})" + resource.key = key.public_key + end + + deploy_key_name = "DEPLOY_KEY_#{key.name}_#{key.bits}" + + Factory::Resource::SecretVariable.fabricate! do |resource| + resource.project = @project + resource.key = deploy_key_name + resource.value = key.private_key + end + + gitlab_ci = <<~YAML + cat-config: + script: + - mkdir -p ~/.ssh + - ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts + - eval $(ssh-agent -s) + - ssh-add -D + - echo "$#{deploy_key_name}" | ssh-add - + - git clone #{@repository_location.git_uri} + - cd #{@project.name} + - git checkout #{deploy_key_name} + - sha1sum .gitlab-ci.yml + tags: + - qa + - docker + YAML + + Factory::Repository::ProjectPush.fabricate! do |resource| + resource.project = @project + resource.file_name = '.gitlab-ci.yml' + resource.commit_message = 'Add .gitlab-ci.yml' + resource.file_content = gitlab_ci + resource.branch_name = deploy_key_name + resource.new_branch = true + end + + sha1sum = Digest::SHA1.hexdigest(gitlab_ci) + + Page::Project::Show.act { wait_for_push } + Page::Menu::Side.act { click_ci_cd_pipelines } + Page::Project::Pipeline::Index.act { go_to_latest_pipeline } + Page::Project::Pipeline::Show.act { go_to_first_job } + + Page::Project::Job::Show.perform do |job| + job.wait(reload: false) do + job.completed? && !job.trace_loading? + end + + expect(job.passed?).to be_truthy, "Job status did not become \"passed\"." + expect(job.output).to include(sha1sum) + end + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb new file mode 100644 index 00000000000..dd24e8ffba5 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'pathname' + +module QA + context :configure, :orchestrated, :kubernetes do + describe 'Auto DevOps support' do + after do + @cluster&.remove! + end + + it 'user creates a new project and runs auto devops' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + project = Factory::Resource::Project.fabricate! do |p| + p.name = 'project-with-autodevops' + p.description = 'Project with Auto Devops' + end + + # Disable code_quality check in Auto DevOps pipeline as it takes + # too long and times out the test + Factory::Resource::SecretVariable.fabricate! do |resource| + resource.key = 'CODE_QUALITY_DISABLED' + resource.value = '1' + end + + # Create Auto Devops compatible repo + Factory::Repository::ProjectPush.fabricate! do |push| + push.project = project + push.directory = Pathname + .new(__dir__) + .join('../../../fixtures/auto_devops_rack') + push.commit_message = 'Create Auto DevOps compatible rack application' + end + + Page::Project::Show.act { wait_for_push } + + # Create and connect K8s cluster + @cluster = Service::KubernetesCluster.new.create! + kubernetes_cluster = Factory::Resource::KubernetesCluster.fabricate! do |cluster| + cluster.project = project + cluster.cluster = @cluster + cluster.install_helm_tiller = true + cluster.install_ingress = true + cluster.install_prometheus = true + cluster.install_runner = true + end + + project.visit! + Page::Menu::Side.act { click_ci_cd_settings } + Page::Project::Settings::CICD.perform do |p| + p.enable_auto_devops_with_domain("#{kubernetes_cluster.ingress_ip}.nip.io") + end + + project.visit! + Page::Menu::Side.act { click_ci_cd_pipelines } + Page::Project::Pipeline::Index.act { go_to_latest_pipeline } + + Page::Project::Pipeline::Show.perform do |pipeline| + expect(pipeline).to have_build('build', status: :success, wait: 600) + expect(pipeline).to have_build('test', status: :success, wait: 600) + expect(pipeline).to have_build('production', status: :success, wait: 1200) + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb new file mode 100644 index 00000000000..6ffdc55538a --- /dev/null +++ b/qa/qa/specs/features/browser_ui/7_configure/mattermost/create_group_with_mattermost_team_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module QA + context :configure, :orchestrated, :mattermost do + describe 'Mattermost support' do + it 'user creates a group with a mattermost team' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + Page::Menu::Main.act { go_to_groups } + + Page::Dashboard::Groups.perform do |page| + page.go_to_new_group + + expect(page).to have_content( + /Create a Mattermost team for this group/ + ) + end + end + end + end +end diff --git a/qa/qa/specs/features/login/basic_spec.rb b/qa/qa/specs/features/login/basic_spec.rb deleted file mode 100644 index f866466c7bf..00000000000 --- a/qa/qa/specs/features/login/basic_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -module QA - describe 'basic user login', :smoke do - it 'user logs in using basic credentials' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - # TODO, since `Signed in successfully` message was removed - # this is the only way to tell if user is signed in correctly. - # - Page::Menu::Main.perform do |menu| - expect(menu).to have_personal_area - end - end - end -end diff --git a/qa/qa/specs/features/login/ldap_spec.rb b/qa/qa/specs/features/login/ldap_spec.rb deleted file mode 100644 index de6111eea64..00000000000 --- a/qa/qa/specs/features/login/ldap_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -module QA - describe 'LDAP user login', :orchestrated, :ldap do - before do - Runtime::Env.user_type = 'ldap' - end - - it 'user logs in using LDAP credentials' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - # TODO, since `Signed in successfully` message was removed - # this is the only way to tell if user is signed in correctly. - # - Page::Menu::Main.perform do |menu| - expect(menu).to have_personal_area - end - end - end -end diff --git a/qa/qa/specs/features/mattermost/group_create_spec.rb b/qa/qa/specs/features/mattermost/group_create_spec.rb deleted file mode 100644 index 097e1713aef..00000000000 --- a/qa/qa/specs/features/mattermost/group_create_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -module QA - describe 'create a new group', :orchestrated, :mattermost do - it 'creating a group with a mattermost team' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - Page::Menu::Main.act { go_to_groups } - - Page::Dashboard::Groups.perform do |page| - page.go_to_new_group - - expect(page).to have_content( - /Create a Mattermost team for this group/ - ) - end - end - end -end diff --git a/qa/qa/specs/features/mattermost/login_spec.rb b/qa/qa/specs/features/mattermost/login_spec.rb deleted file mode 100644 index 27f7d4c245f..00000000000 --- a/qa/qa/specs/features/mattermost/login_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -module QA - describe 'logging in to Mattermost', :orchestrated, :mattermost do - it 'can use gitlab oauth' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) do - Page::Main::Login.act { sign_in_using_credentials } - - Runtime::Browser.visit(:mattermost, Page::Mattermost::Login) do - Page::Mattermost::Login.act { sign_in_using_oauth } - - Page::Mattermost::Main.perform do |page| - expect(page).to have_content(/(Welcome to: Mattermost|Logout GitLab Mattermost)/) - end - end - end - end - end -end diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb deleted file mode 100644 index 71e79956b85..00000000000 --- a/qa/qa/specs/features/merge_request/create_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -module QA - describe 'creates a merge request with milestone' do - it 'user creates a new merge request' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - current_project = Factory::Resource::Project.fabricate! do |project| - project.name = 'project-with-merge-request-and-milestone' - end - - current_milestone = Factory::Resource::ProjectMilestone.fabricate! do |milestone| - milestone.title = 'unique-milestone' - milestone.project = current_project - end - - Factory::Resource::MergeRequest.fabricate! do |merge_request| - merge_request.title = 'This is a merge request with a milestone' - merge_request.description = 'Great feature with milestone' - merge_request.project = current_project - merge_request.milestone = current_milestone - end - - expect(page).to have_content('This is a merge request with a milestone') - expect(page).to have_content('Great feature with milestone') - expect(page).to have_content(/Opened [\w\s]+ ago/) - - Page::Issuable::Sidebar.perform do |sidebar| - expect(sidebar).to have_milestone(current_milestone.title) - end - end - end - - describe 'creates a merge request', :smoke do - it 'user creates a new merge request' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - current_project = Factory::Resource::Project.fabricate! do |project| - project.name = 'project-with-merge-request' - end - - Factory::Resource::MergeRequest.fabricate! do |merge_request| - merge_request.title = 'This is a merge request' - merge_request.description = 'Great feature' - merge_request.project = current_project - end - - expect(page).to have_content('This is a merge request') - expect(page).to have_content('Great feature') - expect(page).to have_content(/Opened [\w\s]+ ago/) - end - end -end diff --git a/qa/qa/specs/features/merge_request/rebase_spec.rb b/qa/qa/specs/features/merge_request/rebase_spec.rb deleted file mode 100644 index c36d28e4237..00000000000 --- a/qa/qa/specs/features/merge_request/rebase_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -module QA - describe 'merge request rebase' do - it 'rebases source branch of merge request' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - project = Factory::Resource::Project.fabricate! do |project| - project.name = "only-fast-forward" - end - - Page::Menu::Side.act { go_to_settings } - Page::Project::Settings::MergeRequest.act { enable_ff_only } - - merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request| - merge_request.project = project - merge_request.title = 'Needs rebasing' - end - - Factory::Repository::ProjectPush.fabricate! do |push| - push.project = project - push.file_name = "other.txt" - push.file_content = "New file added!" - end - - merge_request.visit! - - Page::MergeRequest::Show.perform do |merge_request| - expect(merge_request).to have_content('Needs rebasing') - expect(merge_request).not_to be_fast_forward_possible - expect(merge_request).not_to have_merge_button - - merge_request.rebase! - - expect(merge_request).to have_merge_button - expect(merge_request.fast_forward_possible?).to be_truthy - end - end - end -end diff --git a/qa/qa/specs/features/merge_request/squash_spec.rb b/qa/qa/specs/features/merge_request/squash_spec.rb deleted file mode 100644 index 3ecc36a5ae1..00000000000 --- a/qa/qa/specs/features/merge_request/squash_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -module QA - describe 'merge request squash commits' do - it 'when squash commits is marked before merge' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - project = Factory::Resource::Project.fabricate! do |project| - project.name = "squash-before-merge" - end - - merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request| - merge_request.project = project - merge_request.title = 'Squashing commits' - end - - Factory::Repository::ProjectPush.fabricate! do |push| - push.project = project - push.commit_message = 'to be squashed' - push.branch_name = merge_request.source_branch - push.new_branch = false - push.file_name = 'other.txt' - push.file_content = "Test with unicode characters ❤✓€❄" - end - - merge_request.visit! - - expect(page).to have_text('to be squashed') - - Page::MergeRequest::Show.perform do |merge_request_page| - merge_request_page.mark_to_squash - merge_request_page.merge! - - merge_request.project.visit! - - Git::Repository.perform do |repository| - repository.uri = Page::Project::Show.act do - choose_repository_clone_http - repository_location.uri - end - - repository.use_default_credentials - - repository.act { clone } - - expect(repository.commits.size).to eq 3 - end - end - end - end -end diff --git a/qa/qa/specs/features/project/activity_spec.rb b/qa/qa/specs/features/project/activity_spec.rb deleted file mode 100644 index c7ce8dfdcc6..00000000000 --- a/qa/qa/specs/features/project/activity_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -module QA - describe 'activity page' do - it 'push creates an event in the activity page' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - Factory::Repository::ProjectPush.fabricate! do |push| - push.file_name = 'README.md' - push.file_content = '# This is a test project' - push.commit_message = 'Add README.md' - end - - Page::Menu::Side.act { go_to_activity } - - Page::Project::Activity.act { go_to_push_events } - - expect(page).to have_content('pushed new branch master') - end - end -end diff --git a/qa/qa/specs/features/project/add_deploy_key_spec.rb b/qa/qa/specs/features/project/add_deploy_key_spec.rb deleted file mode 100644 index 24f9f4c77f8..00000000000 --- a/qa/qa/specs/features/project/add_deploy_key_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -module QA - describe 'deploy keys support' do - it 'user adds a deploy key' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - key = Runtime::Key::RSA.new - deploy_key_title = 'deploy key title' - deploy_key_value = key.public_key - - deploy_key = Factory::Resource::DeployKey.fabricate! do |resource| - resource.title = deploy_key_title - resource.key = deploy_key_value - end - - expect(deploy_key.fingerprint).to eq(key.fingerprint) - end - end -end diff --git a/qa/qa/specs/features/project/add_secret_variable_spec.rb b/qa/qa/specs/features/project/add_secret_variable_spec.rb deleted file mode 100644 index 04d9fe488e2..00000000000 --- a/qa/qa/specs/features/project/add_secret_variable_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -module QA - describe 'secret variables support' do - it 'user adds a secret variable' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - Factory::Resource::SecretVariable.fabricate! do |resource| - resource.key = 'VARIABLE_KEY' - resource.value = 'some secret variable' - end - - Page::Project::Settings::CICD.perform do |settings| - settings.expand_secret_variables do |page| - expect(page).to have_field(with: 'VARIABLE_KEY') - expect(page).not_to have_field(with: 'some secret variable') - - page.reveal_variables - - expect(page).to have_field(with: 'some secret variable') - end - end - end - end -end diff --git a/qa/qa/specs/features/project/auto_devops_spec.rb b/qa/qa/specs/features/project/auto_devops_spec.rb deleted file mode 100644 index 248669b6046..00000000000 --- a/qa/qa/specs/features/project/auto_devops_spec.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'pathname' - -module QA - describe 'Auto Devops', :orchestrated, :kubernetes do - after do - @cluster&.remove! - end - - it 'user creates a new project and runs auto devops' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - project = Factory::Resource::Project.fabricate! do |p| - p.name = 'project-with-autodevops' - p.description = 'Project with Auto Devops' - end - - # Disable code_quality check in Auto DevOps pipeline as it takes - # too long and times out the test - Factory::Resource::SecretVariable.fabricate! do |resource| - resource.key = 'CODE_QUALITY_DISABLED' - resource.value = '1' - end - - # Create Auto Devops compatible repo - Factory::Repository::ProjectPush.fabricate! do |push| - push.project = project - push.directory = Pathname - .new(__dir__) - .join('../../../fixtures/auto_devops_rack') - push.commit_message = 'Create Auto DevOps compatible rack application' - end - - Page::Project::Show.act { wait_for_push } - - # Create and connect K8s cluster - @cluster = Service::KubernetesCluster.new.create! - kubernetes_cluster = Factory::Resource::KubernetesCluster.fabricate! do |cluster| - cluster.project = project - cluster.cluster = @cluster - cluster.install_helm_tiller = true - cluster.install_ingress = true - cluster.install_prometheus = true - cluster.install_runner = true - end - - project.visit! - Page::Menu::Side.act { click_ci_cd_settings } - Page::Project::Settings::CICD.perform do |p| - p.enable_auto_devops_with_domain("#{kubernetes_cluster.ingress_ip}.nip.io") - end - - project.visit! - Page::Menu::Side.act { click_ci_cd_pipelines } - Page::Project::Pipeline::Index.act { go_to_latest_pipeline } - - Page::Project::Pipeline::Show.perform do |pipeline| - expect(pipeline).to have_build('build', status: :success, wait: 600) - expect(pipeline).to have_build('test', status: :success, wait: 600) - expect(pipeline).to have_build('production', status: :success, wait: 1200) - end - end - end -end diff --git a/qa/qa/specs/features/project/create_issue_spec.rb b/qa/qa/specs/features/project/create_issue_spec.rb deleted file mode 100644 index 793e7db87cb..00000000000 --- a/qa/qa/specs/features/project/create_issue_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -module QA - describe 'creates issue', :smoke do - let(:issue_title) { 'issue title' } - - it 'user creates issue' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - Factory::Resource::Issue.fabricate! do |issue| - issue.title = issue_title - end - - Page::Menu::Side.act { click_issues } - - expect(page).to have_content(issue_title) - end - end -end diff --git a/qa/qa/specs/features/project/create_spec.rb b/qa/qa/specs/features/project/create_spec.rb deleted file mode 100644 index 5e19e490778..00000000000 --- a/qa/qa/specs/features/project/create_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -module QA - describe 'create a new project', :smoke do - it 'user creates a new project' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - created_project = Factory::Resource::Project.fabricate! do |project| - project.name = 'awesome-project' - project.description = 'create awesome project test' - end - - expect(created_project.name).to match /^awesome-project-\h{16}$/ - - expect(page).to have_content( - /Project \S?awesome-project\S+ was successfully created/ - ) - - expect(page).to have_content('create awesome project test') - expect(page).to have_content('The repository for this project is empty') - end - end -end diff --git a/qa/qa/specs/features/project/deploy_key_clone_spec.rb b/qa/qa/specs/features/project/deploy_key_clone_spec.rb deleted file mode 100644 index 1d099508c24..00000000000 --- a/qa/qa/specs/features/project/deploy_key_clone_spec.rb +++ /dev/null @@ -1,105 +0,0 @@ -require 'digest/sha1' - -module QA - describe 'cloning code using a deploy key', :docker do - def login - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - end - - before(:all) do - login - - @runner_name = "qa-runner-#{Time.now.to_i}" - - @project = Factory::Resource::Project.fabricate! do |resource| - resource.name = 'deploy-key-clone-project' - end - - @repository_location = @project.repository_ssh_location - - Factory::Resource::Runner.fabricate! do |resource| - resource.project = @project - resource.name = @runner_name - resource.tags = %w[qa docker] - resource.image = 'gitlab/gitlab-runner:ubuntu' - end - - Page::Menu::Main.act { sign_out } - end - - after(:all) do - Service::Runner.new(@runner_name).remove! - end - - keys = [ - [Runtime::Key::RSA, 8192], - [Runtime::Key::ECDSA, 521], - [Runtime::Key::ED25519] - ] - - keys.each do |(key_class, bits)| - it "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do - key = key_class.new(*bits) - - login - - Factory::Resource::DeployKey.fabricate! do |resource| - resource.project = @project - resource.title = "deploy key #{key.name}(#{key.bits})" - resource.key = key.public_key - end - - deploy_key_name = "DEPLOY_KEY_#{key.name}_#{key.bits}" - - Factory::Resource::SecretVariable.fabricate! do |resource| - resource.project = @project - resource.key = deploy_key_name - resource.value = key.private_key - end - - gitlab_ci = <<~YAML - cat-config: - script: - - mkdir -p ~/.ssh - - ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts - - eval $(ssh-agent -s) - - ssh-add -D - - echo "$#{deploy_key_name}" | ssh-add - - - git clone #{@repository_location.git_uri} - - cd #{@project.name} - - git checkout #{deploy_key_name} - - sha1sum .gitlab-ci.yml - tags: - - qa - - docker - YAML - - Factory::Repository::ProjectPush.fabricate! do |resource| - resource.project = @project - resource.file_name = '.gitlab-ci.yml' - resource.commit_message = 'Add .gitlab-ci.yml' - resource.file_content = gitlab_ci - resource.branch_name = deploy_key_name - resource.new_branch = true - end - - sha1sum = Digest::SHA1.hexdigest(gitlab_ci) - - Page::Project::Show.act { wait_for_push } - Page::Menu::Side.act { click_ci_cd_pipelines } - Page::Project::Pipeline::Index.act { go_to_latest_pipeline } - Page::Project::Pipeline::Show.act { go_to_first_job } - - Page::Project::Job::Show.perform do |job| - job.wait(reload: false) do - job.completed? && !job.trace_loading? - end - - expect(job.passed?).to be_truthy, "Job status did not become \"passed\"." - expect(job.output).to include(sha1sum) - end - end - end - end -end diff --git a/qa/qa/specs/features/project/file_spec.rb b/qa/qa/specs/features/project/file_spec.rb deleted file mode 100644 index 5659784cd5c..00000000000 --- a/qa/qa/specs/features/project/file_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -module QA - describe 'File Functionality', :core do - it 'lets a user create, edit and delete a file via WebUI' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - # Create - file_name = 'QA Test - File name' - file_content = 'QA Test - File content' - commit_message_for_create = 'QA Test - Create new file' - - Factory::Resource::File.fabricate! do |file| - file.name = file_name - file.content = file_content - file.commit_message = commit_message_for_create - end - - expect(page).to have_content('The file has been successfully created.') - expect(page).to have_content(file_name) - expect(page).to have_content(file_content) - expect(page).to have_content(commit_message_for_create) - - # Edit - updated_file_content = 'QA Test - Updated file content' - commit_message_for_update = 'QA Test - Update file' - - Page::File::Show.act { click_edit } - - Page::File::Form.act do - remove_content - add_content(updated_file_content) - add_commit_message(commit_message_for_update) - commit_changes - end - - expect(page).to have_content('Your changes have been successfully committed.') - expect(page).to have_content(updated_file_content) - expect(page).to have_content(commit_message_for_update) - - # Delete - commit_message_for_delete = 'QA Test - Delete file' - - Page::File::Show.act do - click_delete - add_commit_message(commit_message_for_delete) - click_delete_file - end - - expect(page).to have_content('The file has been successfully deleted.') - expect(page).to have_content(commit_message_for_delete) - expect(page).to have_no_content(file_name) - end - end -end diff --git a/qa/qa/specs/features/project/fork_project_spec.rb b/qa/qa/specs/features/project/fork_project_spec.rb deleted file mode 100644 index 280978bb950..00000000000 --- a/qa/qa/specs/features/project/fork_project_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -module QA - describe 'Project fork' do - it 'can submit merge requests to upstream master' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - merge_request = Factory::Resource::MergeRequestFromFork.fabricate! do |merge_request| - merge_request.fork_branch = 'feature-branch' - end - - Page::Menu::Main.perform { |main| main.sign_out } - Page::Main::Login.perform { |login| login.sign_in_using_credentials } - - merge_request.visit! - - Page::MergeRequest::Show.perform { |show| show.merge! } - - expect(page).to have_content('The changes were merged') - end - end -end diff --git a/qa/qa/specs/features/project/import_from_github_spec.rb b/qa/qa/specs/features/project/import_from_github_spec.rb deleted file mode 100644 index 57695d2c726..00000000000 --- a/qa/qa/specs/features/project/import_from_github_spec.rb +++ /dev/null @@ -1,106 +0,0 @@ -module QA - describe 'user imports a GitHub repo', :orchestrated, :github do - let(:imported_project) do - Factory::Resource::ProjectImportedFromGithub.fabricate! do |project| - project.name = 'imported-project' - project.personal_access_token = Runtime::Env.github_access_token - project.github_repository_path = 'gitlab-qa/test-project' - end - end - - after do - # We need to delete the imported project because it's impossible to import - # the same GitHub project twice for a given user. - api_client = Runtime::API::Client.new(:gitlab) - delete_project_request = Runtime::API::Request.new(api_client, "/projects/#{CGI.escape("#{Runtime::Namespace.path}/#{imported_project.name}")}") - delete delete_project_request.url - - expect_status(202) - end - - it 'user imports a GitHub repo' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - imported_project # import the project - - Page::Menu::Main.act { go_to_projects } - Page::Dashboard::Projects.perform do |dashboard| - dashboard.go_to_project(imported_project.name) - end - - Page::Project::Show.act { wait_for_import } - - verify_repository_import - verify_issues_import - verify_merge_requests_import - verify_labels_import - verify_milestones_import - verify_wiki_import - end - - def verify_repository_import - expect(page).to have_content('This test project is used for automated GitHub import by GitLab QA.') - expect(page).to have_content(imported_project.name) - end - - def verify_issues_import - Page::Menu::Side.act { click_issues } - expect(page).to have_content('This is a sample issue') - - click_link 'This is a sample issue' - - expect(page).to have_content('We should populate this project with issues, pull requests and wiki pages.') - - # Comments - expect(page).to have_content('This is a comment from @rymai.') - - Page::Issuable::Sidebar.perform do |issuable| - expect(issuable).to have_label('enhancement') - expect(issuable).to have_label('help wanted') - expect(issuable).to have_label('good first issue') - end - end - - def verify_merge_requests_import - Page::Menu::Side.act { click_merge_requests } - expect(page).to have_content('Improve README.md') - - click_link 'Improve README.md' - - expect(page).to have_content('This improves the README file a bit.') - - # Review comment are not supported yet - expect(page).not_to have_content('Really nice change.') - - # Comments - expect(page).to have_content('Nice work! This is a comment from @rymai.') - - # Diff comments - expect(page).to have_content('[Review comment] I like that!') - expect(page).to have_content('[Review comment] Nice blank line.') - expect(page).to have_content('[Single diff comment] Much better without this line!') - - Page::Issuable::Sidebar.perform do |issuable| - expect(issuable).to have_label('bug') - expect(issuable).to have_label('enhancement') - end - end - - def verify_labels_import - # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19228 - # to build upon it. - end - - def verify_milestones_import - # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18727 - # to build upon it. - end - - def verify_wiki_import - Page::Menu::Side.act { click_wiki } - - expect(page).to have_content('Welcome to the test-project wiki!') - end - end -end diff --git a/qa/qa/specs/features/project/pipelines_spec.rb b/qa/qa/specs/features/project/pipelines_spec.rb deleted file mode 100644 index 6c6b4e80626..00000000000 --- a/qa/qa/specs/features/project/pipelines_spec.rb +++ /dev/null @@ -1,102 +0,0 @@ -module QA - describe 'CI/CD Pipelines', :orchestrated, :docker do - let(:executor) { "qa-runner-#{Time.now.to_i}" } - - after do - Service::Runner.new(executor).remove! - end - - it 'user registers a new specific runner' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - Factory::Resource::Runner.fabricate! do |runner| - runner.name = executor - end - - Page::Project::Settings::CICD.perform do |settings| - sleep 5 # Runner should register within 5 seconds - settings.refresh - - settings.expand_runners_settings do |page| - expect(page).to have_content(executor) - expect(page).to have_online_runner - end - end - end - - it 'users creates a new pipeline' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - project = Factory::Resource::Project.fabricate! do |project| - project.name = 'project-with-pipelines' - project.description = 'Project with CI/CD Pipelines.' - end - - Factory::Resource::Runner.fabricate! do |runner| - runner.project = project - runner.name = executor - runner.tags = %w[qa test] - end - - Factory::Repository::ProjectPush.fabricate! do |push| - push.project = project - push.file_name = '.gitlab-ci.yml' - push.commit_message = 'Add .gitlab-ci.yml' - push.file_content = <<~EOF - test-success: - tags: - - qa - - test - script: echo 'OK' - - test-failure: - tags: - - qa - - test - script: - - echo 'FAILURE' - - exit 1 - - test-tags: - tags: - - qa - - docker - script: echo 'NOOP' - - test-artifacts: - tags: - - qa - - test - script: mkdir my-artifacts; echo "CONTENTS" > my-artifacts/artifact.txt - artifacts: - paths: - - my-artifacts/ - EOF - end - - Page::Project::Show.act { wait_for_push } - - expect(page).to have_content('Add .gitlab-ci.yml') - - Page::Menu::Side.act { click_ci_cd_pipelines } - - expect(page).to have_content('All 1') - expect(page).to have_content('Add .gitlab-ci.yml') - - puts 'Waiting for the runner to process the pipeline' - sleep 15 # Runner should process all jobs within 15 seconds. - - Page::Project::Pipeline::Index.act { go_to_latest_pipeline } - - Page::Project::Pipeline::Show.perform do |pipeline| - expect(pipeline).to be_running - expect(pipeline).to have_build('test-success', status: :success) - expect(pipeline).to have_build('test-failure', status: :failed) - expect(pipeline).to have_build('test-tags', status: :pending) - expect(pipeline).to have_build('test-artifacts', status: :success) - end - end - end -end diff --git a/qa/qa/specs/features/project/wikis_spec.rb b/qa/qa/specs/features/project/wikis_spec.rb deleted file mode 100644 index 9af2dbd1264..00000000000 --- a/qa/qa/specs/features/project/wikis_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -module QA - describe 'Wiki Functionality' do - def login - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - end - - def validate_content(content) - expect(page).to have_content('Wiki was successfully updated') - expect(page).to have_content(/#{content}/) - end - - before do - login - end - - it 'User creates, edits, clones, and pushes to the wiki' do - wiki = Factory::Resource::Wiki.fabricate! do |resource| - resource.title = 'Home' - resource.content = '# My First Wiki Content' - resource.message = 'Update home' - end - - validate_content('My First Wiki Content') - - Page::Project::Wiki::Edit.act { go_to_edit_page } - Page::Project::Wiki::New.perform do |page| - page.set_content("My Second Wiki Content") - page.save_changes - end - - validate_content('My Second Wiki Content') - - Factory::Repository::WikiPush.fabricate! do |push| - push.wiki = wiki - push.file_name = 'Home.md' - push.file_content = '# My Third Wiki Content' - push.commit_message = 'Update Home.md' - end - Page::Menu::Side.act { click_wiki } - - expect(page).to have_content('My Third Wiki Content') - end - end -end diff --git a/qa/qa/specs/features/repository/protected_branches_spec.rb b/qa/qa/specs/features/repository/protected_branches_spec.rb deleted file mode 100644 index 5ae01a12425..00000000000 --- a/qa/qa/specs/features/repository/protected_branches_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -module QA - describe 'branch protection support', :ldap do - let(:branch_name) { 'protected-branch' } - let(:commit_message) { 'Protected push commit message' } - let(:project) do - Factory::Resource::Project.fabricate! do |resource| - resource.name = 'protected-branch-project' - end - end - - before do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - end - - after do - # We need to clear localStorage because we're using it for the dropdown, - # and capybara doesn't do this for us. - # https://github.com/teamcapybara/capybara/issues/1702 - Capybara.execute_script 'localStorage.clear()' - end - - context 'when developers and maintainers are allowed to push to a protected branch' do - it 'user with push rights successfully pushes to the protected branch' do - create_protected_branch(allow_to_push: true) - - push = push_new_file(branch_name) - - expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/) - end - end - - context 'when developers and maintainers are not allowed to push to a protected branch' do - it 'user without push rights fails to push to the protected branch' do - create_protected_branch(allow_to_push: false) - - push = push_new_file(branch_name) - - expect(push.output) - .to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/) - expect(push.output) - .to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/) - end - end - - def create_protected_branch(allow_to_push:) - Factory::Resource::Branch.fabricate! do |resource| - resource.branch_name = branch_name - resource.project = project - resource.allow_to_push = allow_to_push - resource.protected = true - end - end - - def push_new_file(branch) - Factory::Repository::ProjectPush.fabricate! do |resource| - resource.project = project - resource.file_name = 'new_file.md' - resource.file_content = '# This is a new file' - resource.commit_message = 'Add new_file.md' - resource.branch_name = branch_name - resource.new_branch = false - end - end - end -end diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb index ccc0b906845..5493a33cd2a 100644 --- a/qa/spec/runtime/env_spec.rb +++ b/qa/spec/runtime/env_spec.rb @@ -104,6 +104,8 @@ describe QA::Runtime::Env do describe '.github_access_token' do it 'returns "" if GITHUB_ACCESS_TOKEN is not defined' do + stub_env('GITHUB_ACCESS_TOKEN', nil) + expect(described_class.github_access_token).to eq('') end @@ -115,6 +117,8 @@ describe QA::Runtime::Env do describe '.require_github_access_token!' do it 'raises ArgumentError if GITHUB_ACCESS_TOKEN is not defined' do + stub_env('GITHUB_ACCESS_TOKEN', nil) + expect { described_class.require_github_access_token! }.to raise_error(ArgumentError) end diff --git a/qa/spec/scenario/test/instance/all_spec.rb b/qa/spec/scenario/test/instance/all_spec.rb index 423527e938e..1d96352550b 100644 --- a/qa/spec/scenario/test/instance/all_spec.rb +++ b/qa/spec/scenario/test/instance/all_spec.rb @@ -1,4 +1,10 @@ describe QA::Scenario::Test::Instance::All do + subject do + Class.new(described_class) do + tags :rspec, :foo + end + end + context '#perform' do let(:arguments) { spy('Runtime::Scenario') } let(:release) { spy('Runtime::Release') } @@ -24,7 +30,7 @@ describe QA::Scenario::Test::Instance::All do subject.perform("test") expect(runner).to have_received(:options=) - .with(::File.expand_path('../../../../qa/specs/features', __dir__)) + .with(['--tag', 'rspec,foo', '--', ::File.expand_path('../../../../qa/specs/features', __dir__)]) end end diff --git a/qa/spec/scenario/test/instance/smoke_spec.rb b/qa/spec/scenario/test/instance/smoke_spec.rb index e79d19e8212..386eefae930 100644 --- a/qa/spec/scenario/test/instance/smoke_spec.rb +++ b/qa/spec/scenario/test/instance/smoke_spec.rb @@ -30,7 +30,7 @@ describe QA::Scenario::Test::Instance::Smoke do subject.perform("test") expect(runner).to have_received(:options=) - .with(::File.expand_path('../../../../qa/specs/features', __dir__)) + .with(['--tag', 'smoke', '--', ::File.expand_path('../../../../qa/specs/features', __dir__)]) end end diff --git a/rubocop/cop/line_break_around_conditional_block.rb b/rubocop/cop/line_break_around_conditional_block.rb index 011f2bcf8bf..59fe6e5d98c 100644 --- a/rubocop/cop/line_break_around_conditional_block.rb +++ b/rubocop/cop/line_break_around_conditional_block.rb @@ -48,6 +48,8 @@ module RuboCop MSG = 'Add a line break around conditional blocks' def on_if(node) + # This cop causes errors in haml files, so let's skip those + return if in_haml?(node) return if node.single_line? return unless node.if? || node.unless? @@ -116,6 +118,10 @@ module RuboCop def end_line?(line) line =~ /^\s*(end|})/ end + + def in_haml?(node) + node.location.expression.source_buffer.name.end_with?('.haml.rb') + end end end end diff --git a/rubocop/cop/ruby_interpolation_in_translation.rb b/rubocop/cop/ruby_interpolation_in_translation.rb new file mode 100644 index 00000000000..b9411fcfd6c --- /dev/null +++ b/rubocop/cop/ruby_interpolation_in_translation.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + class RubyInterpolationInTranslation < RuboCop::Cop::Cop + MSG = "Don't use ruby interpolation \#{} inside translated strings, instead use \%{}" + + TRANSLATION_METHODS = ':_ :s_ :N_ :n_' + RUBY_INTERPOLATION_REGEX = /.*\#\{.*\}/ + + def_node_matcher :translation_method?, <<~PATTERN + (send nil? {#{TRANSLATION_METHODS}} $dstr ...) + PATTERN + + def_node_matcher :plural_translation_method?, <<~PATTERN + (send nil? :n_ str $dstr ...) + PATTERN + + def on_send(node) + interpolation = translation_method?(node) || plural_translation_method?(node) + return unless interpolation + + interpolation.descendants.each do |possible_violation| + add_offense(possible_violation, message: MSG) if possible_violation.type != :str + end + end + end + end +end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index 88c9bbf24f4..eaf421a7235 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -28,3 +28,4 @@ require_relative 'cop/rspec/env_assignment' require_relative 'cop/rspec/factories_in_migration_specs' require_relative 'cop/sidekiq_options_queue' require_relative 'cop/destroy_all' +require_relative 'cop/ruby_interpolation_in_translation' diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb index f7068546093..465f3499703 100644 --- a/spec/controllers/groups/milestones_controller_spec.rb +++ b/spec/controllers/groups/milestones_controller_spec.rb @@ -141,6 +141,17 @@ describe Groups::MilestonesController do end end + describe "#destroy" do + let(:milestone) { create(:milestone, group: group) } + + it "removes milestone" do + delete :destroy, group_id: group.to_param, id: milestone.iid, format: :js + + expect(response).to be_success + expect { Milestone.find(milestone.id) }.to raise_exception(ActiveRecord::RecordNotFound) + end + end + describe '#ensure_canonical_path' do before do sign_in(user) diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb index d8995f98575..f8c37c0a676 100644 --- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb @@ -29,6 +29,55 @@ describe Projects::MergeRequests::CreationsController do expect(response).to be_success end end + + context 'merge request with some commits' do + render_views + + let(:large_diff_params) do + { + namespace_id: fork_project.namespace.to_param, + project_id: fork_project, + merge_request: { + source_branch: 'master', + target_branch: 'fix' + } + } + end + + describe 'with artificial limits' do + before do + # Load MergeRequestdiff so stub_const won't override it with its own definition + # See https://github.com/rspec/rspec-mocks/issues/1079 + stub_const("#{MergeRequestDiff}::COMMITS_SAFE_SIZE", 2) + end + + it 'limits total commits' do + get :new, large_diff_params + + expect(response).to be_success + + total = assigns(:total_commit_count) + expect(assigns(:commits)).to be_an Array + expect(total).to be > 0 + expect(assigns(:hidden_commit_count)).to be > 0 + expect(response).to have_gitlab_http_status(200) + expect(response.body).to match %r(<span class="commits-count">2 commits</span>) + end + end + + it 'shows total commits' do + get :new, large_diff_params + + expect(response).to be_success + + total = assigns(:total_commit_count) + expect(assigns(:commits)).to be_an Array + expect(total).to be > 0 + expect(assigns(:hidden_commit_count)).to eq(0) + expect(response).to have_gitlab_http_status(200) + expect(response.body).to match %r(<span class="commits-count">#{total} commits</span>) + end + end end describe 'GET diffs' do diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb index 019e4420212..027349b2f8e 100644 --- a/spec/factories/milestones.rb +++ b/spec/factories/milestones.rb @@ -34,7 +34,7 @@ FactoryBot.define do milestone.project_id = evaluator.project_id elsif evaluator.parent id = evaluator.parent.id - evaluator.parent.is_a?(Group) ? board.group_id = id : evaluator.project_id = id + evaluator.parent.is_a?(Group) ? evaluator.group_id = id : evaluator.project_id = id else milestone.project = create(:project) end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 1db6c75b85b..b2eaeb1c487 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -134,6 +134,7 @@ describe "Admin::Users" do expect(page).to have_content(user.email) expect(page).to have_content(user.name) + expect(page).to have_content(user.id) expect(page).to have_link('Block user', href: block_admin_user_path(user)) expect(page).to have_button('Delete user') expect(page).to have_button('Delete user and contributions') diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb index 0db69432702..d9d67788b38 100644 --- a/spec/features/dashboard/milestones_spec.rb +++ b/spec/features/dashboard/milestones_spec.rb @@ -13,8 +13,10 @@ describe 'Dashboard > Milestones' do describe 'as logged-in user' do let(:user) { create(:user) } + let(:group) { create(:group) } let(:project) { create(:project, namespace: user.namespace) } let!(:milestone) { create(:milestone, project: project) } + let!(:milestone2) { create(:milestone, group: group) } before do project.add_maintainer(user) sign_in(user) @@ -24,6 +26,7 @@ describe 'Dashboard > Milestones' do it 'sees milestones' do expect(current_path).to eq dashboard_milestones_path expect(page).to have_content(milestone.title) + expect(page).to have_content(group.name) end end end diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb index d6e7ff33d5d..0c15febe8df 100644 --- a/spec/features/merge_request/user_sees_diff_spec.rb +++ b/spec/features/merge_request/user_sees_diff_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' describe 'Merge request > User sees diff', :js do include ProjectForksHelper + include RepoHelpers let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } @@ -81,5 +82,58 @@ describe 'Merge request > User sees diff', :js do expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1) end end + + context 'when file contains html' do + let(:current_user) { project.owner } + let(:branch_name) {"test_branch"} + + def create_file(branch_name, file_name, content) + Files::CreateService.new( + project, + current_user, + start_branch: branch_name, + branch_name: branch_name, + commit_message: "Create file", + file_path: file_name, + file_content: content + ).execute + + project.commit(branch_name) + end + + it 'escapes any HTML special characters in the diff chunk header' do + file_content = + <<~CONTENT + function foo<input> { + let a = 1; + let b = 2; + let c = 3; + let d = 3; + } + CONTENT + + new_file_content = + <<~CONTENT + function foo<input> { + let a = 1; + let b = 2; + let c = 3; + let x = 3; + } + CONTENT + + file_name = 'xss_file.txt' + + create_file('master', file_name, file_content) + merge_request = create(:merge_request, source_project: project) + create_file(merge_request.source_branch, file_name, new_file_content) + + project.commit(merge_request.source_branch) + + visit diffs_project_merge_request_path(project, merge_request) + + expect(page).to have_text("function foo<input> {") + end + end end end diff --git a/spec/features/milestones/user_deletes_milestone_spec.rb b/spec/features/milestones/user_deletes_milestone_spec.rb index 9d4a68239d3..a8c296b4cd2 100644 --- a/spec/features/milestones/user_deletes_milestone_spec.rb +++ b/spec/features/milestones/user_deletes_milestone_spec.rb @@ -1,26 +1,46 @@ require "rails_helper" describe "User deletes milestone", :js do - set(:user) { create(:user) } - set(:project) { create(:project) } - set(:milestone) { create(:milestone, project: project) } + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, namespace: group) } before do - project.add_developer(user) sign_in(user) + end + + context "when milestone belongs to project" do + let!(:milestone) { create(:milestone, parent: project, title: "project milestone") } + + it "deletes milestone" do + project.add_developer(user) + visit(project_milestones_path(project)) + click_link(milestone.title) + click_button("Delete") + click_button("Delete milestone") + + expect(page).to have_content("No milestones to show") + + visit(activity_project_path(project)) - visit(project_milestones_path(project)) + expect(page).to have_content("#{user.name} destroyed milestone") + end end - it "deletes milestone" do - click_link(milestone.title) - click_button("Delete") - click_button("Delete milestone") + context "when milestone belongs to group" do + let!(:milestone_to_be_deleted) { create(:milestone, parent: group, title: "group milestone 1") } + let!(:milestone) { create(:milestone, parent: group, title: "group milestone 2") } - expect(page).to have_content("No milestones to show") + it "deletes milestone" do + group.add_developer(user) + visit(group_milestones_path(group)) - visit(activity_project_path(project)) + click_link(milestone_to_be_deleted.title) + click_button("Delete") + click_button("Delete milestone") - expect(page).to have_content("#{user.name} destroyed milestone") + expect(page).to have_content(milestone.title) + expect(page).not_to have_content(milestone_to_be_deleted) + end end end diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb index 9e60b4995bd..206a3a4fe9a 100644 --- a/spec/features/profiles/user_edit_profile_spec.rb +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -130,5 +130,15 @@ describe 'User edit profile' do visit user_path(user) expect(page).not_to have_selector '.cover-status' end + + it 'displays a default emoji if only message is entered' do + message = 'a status without emoji' + visit(profile_path) + fill_in 'js-status-message-field', with: message + + within('.js-toggle-emoji-menu') do + expect(page).to have_emoji('speech_balloon') + end + end end end diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index 50e957bf12b..2d791947ee9 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -40,7 +40,7 @@ describe 'User browses a job', :js do it 'displays the failure reason' do within('.builds-container') do build_link = first('.build-job > a') - expect(build_link['data-title']).to eq('test - failed <br> (unknown failure)') + expect(build_link['data-title']).to eq('test - failed - (unknown failure)') end end end @@ -51,7 +51,7 @@ describe 'User browses a job', :js do it 'displays the failure reason and retried label' do within('.builds-container') do build_link = first('.build-job > a') - expect(build_link['data-title']).to eq('test - failed <br> (unknown failure) (retried)') + expect(build_link['data-title']).to eq('test - failed - (unknown failure) (retried)') end end end diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index 08786fe1630..ebc20d15d67 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -36,7 +36,7 @@ describe 'User browses jobs' do it 'displays a tooltip with the failure reason' do page.within('.ci-table') do failed_job_link = page.find('.ci-failed') - expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)') + expect(failed_job_link[:title]).to eq('Failed - (unknown failure)') end end end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index a84492ea5f1..603503a531c 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -126,7 +126,7 @@ describe 'Pipeline', :js do it 'should include the failure reason' do page.within('#ci-badge-test') do build_link = page.find('.js-pipeline-graph-job-link') - expect(build_link['data-original-title']).to eq('test - failed <br> (unknown failure)') + expect(build_link['data-original-title']).to eq('test - failed - (unknown failure)') end end end @@ -319,7 +319,7 @@ describe 'Pipeline', :js do it 'displays a tooltip with the failure reason' do page.within('.ci-table') do failed_job_link = page.find('.ci-failed') - expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)') + expect(failed_job_link[:title]).to eq('Failed - (unknown failure)') end end end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 4a83bcc3efb..26a92f14787 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -406,7 +406,7 @@ describe 'Pipelines', :js do within('.js-builds-dropdown-list') do build_element = page.find('.mini-pipeline-graph-dropdown-item') - expect(build_element['data-original-title']).to eq('build - failed <br> (unknown failure)') + expect(build_element['data-original-title']).to eq('build - failed - (unknown failure)') end end end diff --git a/spec/helpers/import_helper_spec.rb b/spec/helpers/import_helper_spec.rb index 033155617c6..cb0ea4e26ba 100644 --- a/spec/helpers/import_helper_spec.rb +++ b/spec/helpers/import_helper_spec.rb @@ -1,6 +1,16 @@ require 'rails_helper' describe ImportHelper do + describe '#sanitize_project_name' do + it 'removes whitespace' do + expect(helper.sanitize_project_name('my test repo')).to eq('my-test-repo') + end + + it 'removes disallowed characters' do + expect(helper.sanitize_project_name('Test&me$over*h_ere')).to eq('Test-me-over-h_ere') + end + end + describe '#import_project_target' do let(:user) { create(:user) } diff --git a/spec/javascripts/badges/dummy_badge.js b/spec/javascripts/badges/dummy_badge.js index 6aaff21c503..f0cdaddbd33 100644 --- a/spec/javascripts/badges/dummy_badge.js +++ b/spec/javascripts/badges/dummy_badge.js @@ -1,8 +1,9 @@ +import _ from 'underscore'; import { PROJECT_BADGE } from '~/badges/constants'; import { DUMMY_IMAGE_URL, TEST_HOST } from 'spec/test_constants'; export const createDummyBadge = () => { - const id = Math.floor(1000 * Math.random()); + const id = _.uniqueId(); return { id, imageUrl: `${TEST_HOST}/badges/${id}/image/url`, diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js index 7198dbd4cf2..0a8e5a628c0 100644 --- a/spec/javascripts/flash_spec.js +++ b/spec/javascripts/flash_spec.js @@ -57,8 +57,11 @@ describe('Flash', () => { hideFlash(el); expect( - el.style.transition, - ).toBe('opacity 0.3s'); + el.style['transition-property'], + ).toBe('opacity'); + expect( + el.style['transition-duration'], + ).toBe('0.3s'); }); it('sets opacity style', () => { diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js index 1057f0aca3e..6848c95d95d 100644 --- a/spec/javascripts/helpers/vue_mount_component_helper.js +++ b/spec/javascripts/helpers/vue_mount_component_helper.js @@ -1,3 +1,5 @@ +import Vue from 'vue'; + const mountComponent = (Component, props = {}, el = null) => new Component({ propsData: props, @@ -25,4 +27,12 @@ export const mountComponentWithSlots = (Component, { props, slots }) => { return component.$mount(); }; +/** + * Mount a component with the given render method. + * + * This helps with inserting slots that need to be compiled. + */ +export const mountComponentWithRender = (render, el = null) => + mountComponent(Vue.extend({ render }), {}, el); + export default mountComponent; diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js index 24a7d76f30b..06b8b452319 100644 --- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js @@ -184,7 +184,7 @@ describe('IDE commit module actions', () => { branch, }) .then(() => { - expect(f.lastCommit.message).toBe(data.message); + expect(f.lastCommitSha).toBe(data.id); }) .then(done) .catch(done.fail); @@ -266,19 +266,21 @@ describe('IDE commit module actions', () => { }); describe('success', () => { + const COMMIT_RESPONSE = { + id: '123456', + short_id: '123', + message: 'test message', + committed_date: 'date', + stats: { + additions: '1', + deletions: '2', + }, + }; + beforeEach(() => { spyOn(service, 'commit').and.returnValue( Promise.resolve({ - data: { - id: '123456', - short_id: '123', - message: 'test message', - committed_date: 'date', - stats: { - additions: '1', - deletions: '2', - }, - }, + data: COMMIT_RESPONSE, }), ); }); @@ -352,8 +354,8 @@ describe('IDE commit module actions', () => { store .dispatch('commit/commitChanges') .then(() => { - expect(store.state.entries[store.state.openFiles[0].path].lastCommit.message).toBe( - 'test message', + expect(store.state.entries[store.state.openFiles[0].path].lastCommitSha).toBe( + COMMIT_RESPONSE.id, ); done(); diff --git a/spec/javascripts/jobs/components/environments_block_spec.js b/spec/javascripts/jobs/components/environments_block_spec.js new file mode 100644 index 00000000000..015c26be9fc --- /dev/null +++ b/spec/javascripts/jobs/components/environments_block_spec.js @@ -0,0 +1,137 @@ +import Vue from 'vue'; +import component from '~/jobs/components/environments_block.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; + +describe('Environments block', () => { + const Component = Vue.extend(component); + let vm; + const icon = { + group: 'success', + icon: 'status_success', + label: 'passed', + text: 'passed', + tooltip: 'passed', + }; + const deployment = { + path: 'deployment', + name: 'deployment name', + }; + const environment = { + path: '/environment', + name: 'environment', + }; + + afterEach(() => { + vm.$destroy(); + }); + + describe('with latest deployment', () => { + it('renders info for most recent deployment', () => { + vm = mountComponent(Component, { + deploymentStatus: { + status: 'latest', + icon, + deployment, + environment, + }, + }); + + expect(vm.$el.textContent.trim()).toEqual( + 'This job is the most recent deployment to environment.', + ); + }); + }); + + describe('with out of date deployment', () => { + describe('with last deployment', () => { + it('renders info for out date and most recent', () => { + vm = mountComponent(Component, { + deploymentStatus: { + status: 'out_of_date', + icon, + deployment, + environment: Object.assign({}, environment, { + last_deployment: { name: 'deployment', path: 'last_deployment' }, + }), + }, + }); + + expect(vm.$el.textContent.trim()).toEqual( + 'This job is an out-of-date deployment to environment. View the most recent deployment deployment.', + ); + }); + }); + + describe('without last deployment', () => { + it('renders info about out of date deployment', () => { + vm = mountComponent(Component, { + deploymentStatus: { + status: 'out_of_date', + icon, + deployment: null, + environment, + }, + }); + + expect(vm.$el.textContent.trim()).toEqual( + 'This job is an out-of-date deployment to environment.', + ); + }); + }); + }); + + describe('with failed deployment', () => { + it('renders info about failed deployment', () => { + vm = mountComponent(Component, { + deploymentStatus: { + status: 'failed', + icon, + deployment: null, + environment, + }, + }); + + expect(vm.$el.textContent.trim()).toEqual( + 'The deployment of this job to environment did not succeed.', + ); + }); + }); + + describe('creating deployment', () => { + describe('with last deployment', () => { + it('renders info about creating deployment and overriding lastest deployment', () => { + vm = mountComponent(Component, { + deploymentStatus: { + status: 'creating', + icon, + deployment, + environment: Object.assign({}, environment, { + last_deployment: { name: 'deployment', path: 'last_deployment' }, + }), + }, + }); + + expect(vm.$el.textContent.trim()).toEqual( + 'This job is creating a deployment to environment and will overwrite the last deployment.', + ); + }); + }); + + describe('without last deployment', () => { + it('renders info about failed deployment', () => { + vm = mountComponent(Component, { + deploymentStatus: { + status: 'creating', + icon, + deployment: null, + environment, + }, + }); + + expect(vm.$el.textContent.trim()).toEqual( + 'This job is creating a deployment to environment.', + ); + }); + }); + }); +}); diff --git a/spec/javascripts/performance_bar/services/performance_bar_service_spec.js b/spec/javascripts/performance_bar/services/performance_bar_service_spec.js new file mode 100644 index 00000000000..bc6947dbe81 --- /dev/null +++ b/spec/javascripts/performance_bar/services/performance_bar_service_spec.js @@ -0,0 +1,63 @@ +import PerformanceBarService from '~/performance_bar/services/performance_bar_service'; + +describe('PerformanceBarService', () => { + describe('callbackParams', () => { + describe('fireCallback', () => { + function fireCallback(response, peekUrl) { + return PerformanceBarService.callbackParams(response, peekUrl)[0]; + } + + it('returns false when the request URL is the peek URL', () => { + expect(fireCallback({ headers: { 'x-request-id': '123' }, url: '/peek' }, '/peek')) + .toBeFalsy(); + }); + + it('returns false when there is no request ID', () => { + expect(fireCallback({ headers: {}, url: '/request' }, '/peek')) + .toBeFalsy(); + }); + + it('returns false when the request is an API request', () => { + expect(fireCallback({ headers: { 'x-request-id': '123' }, url: '/api/' }, '/peek')) + .toBeFalsy(); + }); + + it('returns false when the response is from the cache', () => { + expect(fireCallback({ headers: { 'x-request-id': '123', 'x-gitlab-from-cache': 'true' }, url: '/request' }, '/peek')) + .toBeFalsy(); + }); + + it('returns true when all conditions are met', () => { + expect(fireCallback({ headers: { 'x-request-id': '123' }, url: '/request' }, '/peek')) + .toBeTruthy(); + }); + }); + + describe('requestId', () => { + function requestId(response, peekUrl) { + return PerformanceBarService.callbackParams(response, peekUrl)[1]; + } + + it('gets the request ID from the headers', () => { + expect(requestId({ headers: { 'x-request-id': '123' } }, '/peek')) + .toEqual('123'); + }); + }); + + describe('requestUrl', () => { + function requestUrl(response, peekUrl) { + return PerformanceBarService.callbackParams(response, peekUrl)[2]; + } + + it('gets the request URL from the response object', () => { + expect(requestUrl({ headers: {}, url: '/request' }, '/peek')) + .toEqual('/request'); + }); + + it('gets the request URL from response.config if present', () => { + expect(requestUrl({ headers: {}, config: { url: '/config-url' }, url: '/request' }, '/peek')) + .toEqual('/config-url'); + }); + }); + }); +}); diff --git a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js index ff584396d61..2b47ca236b2 100644 --- a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js +++ b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js @@ -82,12 +82,4 @@ describe('dropdown job component', () => { it('renders dropdown with jobs', () => { expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(mock.jobs.length); }); - - it('escapes tooltip title', () => { - expect( - vm.$el.querySelector('.js-pipeline-graph-job-link').getAttribute('data-original-title'), - ).toEqual( - '<img src=x onerror=alert(document.domain)> - passed', - ); - }); }); diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js index 215ce1e81b5..0ae448f2ea8 100644 --- a/spec/javascripts/pipelines/graph/job_component_spec.js +++ b/spec/javascripts/pipelines/graph/job_component_spec.js @@ -161,24 +161,4 @@ describe('pipeline graph job component', () => { expect(component.$el.querySelector(tooltipBoundary)).toBeNull(); }); }); - - describe('tooltipText', () => { - it('escapes job name', () => { - component = mountComponent(JobComponent, { - job: { - id: 4259, - name: '<img src=x onerror=alert(document.domain)>', - status: { - icon: 'status_success', - label: 'success', - tooltip: 'failed', - }, - }, - }); - - expect( - component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title'), - ).toEqual('<img src=x onerror=alert(document.domain)> - failed'); - }); - }); }); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js index 8ac2f26979b..0e42537faed 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js @@ -45,7 +45,7 @@ describe('MRWidgetHeader', () => { }); }); - describe('commitsText', () => { + describe('commitsBehindText', () => { it('returns singular when there is one commit', () => { vm = mountComponent(Component, { mr: { @@ -53,11 +53,12 @@ describe('MRWidgetHeader', () => { sourceBranch: 'mr-widget-refactor', sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">Link</a>', targetBranch: 'master', + targetBranchPath: '/foo/bar/master', statusPath: 'abc', }, }); - expect(vm.commitsText).toEqual('1 commit behind'); + expect(vm.commitsBehindText).toEqual('The source branch is <a href="/foo/bar/master">1 commit behind</a> the target branch'); }); it('returns plural when there is more than one commit', () => { @@ -67,11 +68,12 @@ describe('MRWidgetHeader', () => { sourceBranch: 'mr-widget-refactor', sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">Link</a>', targetBranch: 'master', + targetBranchPath: '/foo/bar/master', statusPath: 'abc', }, }); - expect(vm.commitsText).toEqual('2 commits behind'); + expect(vm.commitsBehindText).toEqual('The source branch is <a href="/foo/bar/master">2 commits behind</a> the target branch'); }); }); }); @@ -280,9 +282,9 @@ describe('MRWidgetHeader', () => { }); it('renders diverged commits info', () => { - expect(vm.$el.querySelector('.diverged-commits-count').textContent).toMatch( - /(mr-widget-refactor[\s\S]+?is 12 commits behind[\s\S]+?master)/, - ); + expect(vm.$el.querySelector('.diverged-commits-count').textContent).toEqual('The source branch is 12 commits behind the target branch'); + expect(vm.$el.querySelector('.diverged-commits-count a').textContent).toEqual('12 commits behind'); + expect(vm.$el.querySelector('.diverged-commits-count a')).toHaveAttr('href', vm.mr.targetBranchPath); }); }); }); diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js index 076d940961d..f1fe2e996fc 100644 --- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js +++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js @@ -44,7 +44,11 @@ describe('StackedProgressBarComponent', () => { }); it('returns percentage with decimal place from provided count based on `totalCount`', () => { - expect(vm.getPercent(10)).toBe(0.2); + expect(vm.getPercent(67)).toBe(1.3); + }); + + it('returns percentage as `< 1` from provided count based on `totalCount` when evaluated value is less than 1', () => { + expect(vm.getPercent(10)).toBe('< 1'); }); }); diff --git a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js new file mode 100644 index 00000000000..8465757deb6 --- /dev/null +++ b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js @@ -0,0 +1,162 @@ +import { mountComponentWithRender } from 'spec/helpers/vue_mount_component_helper'; +import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; + +const TEST_TITLE = 'lorem-ipsum-dolar-sit-amit-consectur-adipiscing-elit-sed-do'; +const CLASS_SHOW_TOOLTIP = 'js-show-tooltip'; +const STYLE_TRUNCATED = { + display: 'inline-block', + 'max-width': '20px', +}; +const STYLE_NORMAL = { + display: 'inline-block', + 'max-width': '1000px', +}; + +function mountTooltipOnTruncate(options, createChildren) { + return mountComponentWithRender(h => h(TooltipOnTruncate, options, createChildren(h)), '#app'); +} + +describe('TooltipOnTruncate component', () => { + let vm; + + beforeEach(() => { + const el = document.createElement('div'); + el.id = 'app'; + document.body.appendChild(el); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('with default target', () => { + it('renders tooltip if truncated', done => { + const options = { + style: STYLE_TRUNCATED, + props: { + title: TEST_TITLE, + }, + }; + + vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]); + + vm.$nextTick() + .then(() => { + expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP); + expect(vm.$el).toHaveData('original-title', TEST_TITLE); + expect(vm.$el).toHaveData('placement', 'top'); + }) + .then(done) + .catch(done.fail); + }); + + it('does not render tooltip if normal', done => { + const options = { + style: STYLE_NORMAL, + props: { + title: TEST_TITLE, + }, + }; + + vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]); + + vm.$nextTick() + .then(() => { + expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('with child target', () => { + it('renders tooltip if truncated', done => { + const options = { + style: STYLE_NORMAL, + props: { + title: TEST_TITLE, + truncateTarget: 'child', + }, + }; + + vm = mountTooltipOnTruncate(options, (h) => [ + h('a', { style: STYLE_TRUNCATED }, TEST_TITLE), + ]); + + vm.$nextTick() + .then(() => { + expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP); + }) + .then(done) + .catch(done.fail); + }); + + it('does not render tooltip if normal', done => { + const options = { + props: { + title: TEST_TITLE, + truncateTarget: 'child', + }, + }; + + vm = mountTooltipOnTruncate(options, (h) => [ + h('a', { style: STYLE_NORMAL }, TEST_TITLE), + ]); + + vm.$nextTick() + .then(() => { + expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('with fn target', () => { + it('renders tooltip if truncated', done => { + const options = { + style: STYLE_NORMAL, + props: { + title: TEST_TITLE, + truncateTarget: (el) => el.childNodes[1], + }, + }; + + vm = mountTooltipOnTruncate(options, (h) => [ + h('a', { style: STYLE_NORMAL }, TEST_TITLE), + h('span', { style: STYLE_TRUNCATED }, TEST_TITLE), + ]); + + vm.$nextTick() + .then(() => { + expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('placement', () => { + it('sets data-placement when tooltip is rendered', done => { + const options = { + props: { + title: TEST_TITLE, + truncateTarget: 'child', + placement: 'bottom', + }, + }; + + vm = mountTooltipOnTruncate(options, (h) => [ + h('a', { style: STYLE_TRUNCATED }, TEST_TITLE), + ]); + + vm.$nextTick() + .then(() => { + expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP); + expect(vm.$el).toHaveData('placement', options.props.placement); + }) + .then(done) + .catch(done.fail); + }); + }); +}); diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb index f313e675654..8bb5a843484 100644 --- a/spec/lib/feature_spec.rb +++ b/spec/lib/feature_spec.rb @@ -1,6 +1,13 @@ require 'spec_helper' describe Feature do + before do + # We mock all calls to .enabled? to return true in order to force all + # specs to run the feature flag gated behavior, but here we need a clean + # behavior from the class + allow(described_class).to receive(:enabled?).and_call_original + end + describe '.get' do let(:feature) { double(:feature) } let(:key) { 'my_feature' } @@ -106,4 +113,63 @@ describe Feature do it_behaves_like 'a memoized Flipper instance' end end + + describe '.enabled?' do + it 'returns false for undefined feature' do + expect(described_class.enabled?(:some_random_feature_flag)).to be_falsey + end + + it 'returns false for existing disabled feature in the database' do + described_class.disable(:disabled_feature_flag) + + expect(described_class.enabled?(:disabled_feature_flag)).to be_falsey + end + + it 'returns true for existing enabled feature in the database' do + described_class.enable(:enabled_feature_flag) + + expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy + end + + context 'with an individual actor' do + CustomActor = Struct.new(:flipper_id) + + let(:actor) { CustomActor.new(flipper_id: 'CustomActor:5') } + let(:another_actor) { CustomActor.new(flipper_id: 'CustomActor:10') } + + before do + described_class.enable(:enabled_feature_flag, actor) + end + + it 'returns true when same actor is informed' do + expect(described_class.enabled?(:enabled_feature_flag, actor)).to be_truthy + end + + it 'returns false when different actor is informed' do + expect(described_class.enabled?(:enabled_feature_flag, another_actor)).to be_falsey + end + + it 'returns false when no actor is informed' do + expect(described_class.enabled?(:enabled_feature_flag)).to be_falsey + end + end + end + + describe '.disable?' do + it 'returns true for undefined feature' do + expect(described_class.disabled?(:some_random_feature_flag)).to be_truthy + end + + it 'returns true for existing disabled feature in the database' do + described_class.disable(:disabled_feature_flag) + + expect(described_class.disabled?(:disabled_feature_flag)).to be_truthy + end + + it 'returns false for existing enabled feature in the database' do + described_class.enable(:enabled_feature_flag) + + expect(described_class.disabled?(:enabled_feature_flag)).to be_falsey + end + end end diff --git a/spec/lib/gitlab/auth/ldap/access_spec.rb b/spec/lib/gitlab/auth/ldap/access_spec.rb index eff21985108..7800c543cdb 100644 --- a/spec/lib/gitlab/auth/ldap/access_spec.rb +++ b/spec/lib/gitlab/auth/ldap/access_spec.rb @@ -3,51 +3,61 @@ require 'spec_helper' describe Gitlab::Auth::LDAP::Access do include LdapHelpers - let(:access) { described_class.new user } let(:user) { create(:omniauth_user) } + subject(:access) { described_class.new(user) } + describe '.allowed?' do - it 'updates the users `last_credential_check_at' do + before do allow(access).to receive(:update_user) - expect(access).to receive(:allowed?) { true } - expect(described_class).to receive(:open).and_yield(access) + allow(access).to receive(:allowed?).and_return(true) + allow(described_class).to receive(:open).and_yield(access) + end + it "updates the user's `last_credential_check_at`" do expect { described_class.allowed?(user) } .to change { user.last_credential_check_at } end - end - describe '#find_ldap_user' do - it 'finds a user by dn first' do - expect(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(:ldap_user) + it "does not update user's `last_credential_check_at` when in a read-only GitLab instance" do + allow(Gitlab::Database).to receive(:read_only?).and_return(true) - access.find_ldap_user + expect { described_class.allowed?(user) } + .not_to change { user.last_credential_check_at } end end describe '#allowed?' do - subject { access.allowed? } - context 'when the user cannot be found' do before do - allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(nil) - allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_email).and_return(nil) + stub_ldap_person_find_by_dn(nil) + stub_ldap_person_find_by_email(user.email, nil) end - it { is_expected.to be_falsey } + it 'returns false' do + expect(access.allowed?).to be_falsey + end it 'blocks user in GitLab' do - expect(access).to receive(:block_user).with(user, 'does not exist anymore') + access.allowed? + + expect(user).to be_blocked + expect(user).to be_ldap_blocked + end + + it 'logs the reason' do + expect(Gitlab::AppLogger).to receive(:info).with( + "LDAP account \"123456\" does not exist anymore, " \ + "blocking Gitlab user \"#{user.name}\" (#{user.email})" + ) access.allowed? end end context 'when the user is found' do - let(:ldap_user) { Gitlab::Auth::LDAP::Person.new(Net::LDAP::Entry.new, 'ldapmain') } - before do - allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(ldap_user) + stub_ldap_person_find_by_dn(Net::LDAP::Entry.new) end context 'and the user is disabled via active directory' do @@ -55,10 +65,22 @@ describe Gitlab::Auth::LDAP::Access do allow(Gitlab::Auth::LDAP::Person).to receive(:disabled_via_active_directory?).and_return(true) end - it { is_expected.to be_falsey } + it 'returns false' do + expect(access.allowed?).to be_falsey + end it 'blocks user in GitLab' do - expect(access).to receive(:block_user).with(user, 'is disabled in Active Directory') + access.allowed? + + expect(user).to be_blocked + expect(user).to be_ldap_blocked + end + + it 'logs the reason' do + expect(Gitlab::AppLogger).to receive(:info).with( + "LDAP account \"123456\" is disabled in Active Directory, " \ + "blocking Gitlab user \"#{user.name}\" (#{user.email})" + ) access.allowed? end @@ -92,7 +114,17 @@ describe Gitlab::Auth::LDAP::Access do end it 'unblocks user in GitLab' do - expect(access).to receive(:unblock_user).with(user, 'is not disabled anymore') + access.allowed? + + expect(user).not_to be_blocked + expect(user).not_to be_ldap_blocked + end + + it 'logs the reason' do + expect(Gitlab::AppLogger).to receive(:info).with( + "LDAP account \"123456\" is not disabled anymore, " \ + "unblocking Gitlab user \"#{user.name}\" (#{user.email})" + ) access.allowed? end @@ -105,18 +137,32 @@ describe Gitlab::Auth::LDAP::Access do allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive(:active_directory).and_return(false) end - it { is_expected.to be_truthy } + it 'returns true' do + expect(access.allowed?).to be_truthy + end context 'when user cannot be found' do before do - allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(nil) - allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_email).and_return(nil) + stub_ldap_person_find_by_dn(nil) + stub_ldap_person_find_by_email(user.email, nil) end - it { is_expected.to be_falsey } + it 'returns false' do + expect(access.allowed?).to be_falsey + end it 'blocks user in GitLab' do - expect(access).to receive(:block_user).with(user, 'does not exist anymore') + access.allowed? + + expect(user).to be_blocked + expect(user).to be_ldap_blocked + end + + it 'logs the reason' do + expect(Gitlab::AppLogger).to receive(:info).with( + "LDAP account \"123456\" does not exist anymore, " \ + "blocking Gitlab user \"#{user.name}\" (#{user.email})" + ) access.allowed? end @@ -128,7 +174,17 @@ describe Gitlab::Auth::LDAP::Access do end it 'unblocks the user if it exists' do - expect(access).to receive(:unblock_user).with(user, 'is available again') + access.allowed? + + expect(user).not_to be_blocked + expect(user).not_to be_ldap_blocked + end + + it 'logs the reason' do + expect(Gitlab::AppLogger).to receive(:info).with( + "LDAP account \"123456\" is available again, " \ + "unblocking Gitlab user \"#{user.name}\" (#{user.email})" + ) access.allowed? end @@ -152,46 +208,4 @@ describe Gitlab::Auth::LDAP::Access do end end end - - describe '#block_user' do - before do - user.activate - allow(Gitlab::AppLogger).to receive(:info) - - access.block_user user, 'reason' - end - - it 'blocks the user' do - expect(user).to be_blocked - expect(user).to be_ldap_blocked - end - - it 'logs the reason' do - expect(Gitlab::AppLogger).to have_received(:info).with( - "LDAP account \"123456\" reason, " \ - "blocking Gitlab user \"#{user.name}\" (#{user.email})" - ) - end - end - - describe '#unblock_user' do - before do - user.ldap_block - allow(Gitlab::AppLogger).to receive(:info) - - access.unblock_user user, 'reason' - end - - it 'activates the user' do - expect(user).not_to be_blocked - expect(user).not_to be_ldap_blocked - end - - it 'logs the reason' do - Gitlab::AppLogger.info( - "LDAP account \"123456\" reason, " \ - "unblocking Gitlab user \"#{user.name}\" (#{user.email})" - ) - end - end end diff --git a/spec/lib/gitlab/auth/o_auth/provider_spec.rb b/spec/lib/gitlab/auth/o_auth/provider_spec.rb index fc35d430917..80d702cf9dc 100644 --- a/spec/lib/gitlab/auth/o_auth/provider_spec.rb +++ b/spec/lib/gitlab/auth/o_auth/provider_spec.rb @@ -1,6 +1,48 @@ require 'spec_helper' describe Gitlab::Auth::OAuth::Provider do + describe '.enabled?' do + before do + allow(described_class).to receive(:providers).and_return([:ldapmain, :google_oauth2]) + end + + context 'when OmniAuth is disabled' do + before do + allow(Gitlab::Auth).to receive(:omniauth_enabled?).and_return(false) + end + + it 'allows database auth' do + expect(described_class.enabled?('database')).to be_truthy + end + + it 'allows LDAP auth' do + expect(described_class.enabled?('ldapmain')).to be_truthy + end + + it 'does not allow other OmniAuth providers' do + expect(described_class.enabled?('google_oauth2')).to be_falsey + end + end + + context 'when OmniAuth is enabled' do + before do + allow(Gitlab::Auth).to receive(:omniauth_enabled?).and_return(true) + end + + it 'allows database auth' do + expect(described_class.enabled?('database')).to be_truthy + end + + it 'allows LDAP auth' do + expect(described_class.enabled?('ldapmain')).to be_truthy + end + + it 'allows other OmniAuth providers' do + expect(described_class.enabled?('google_oauth2')).to be_truthy + end + end + end + describe '#config_for' do context 'for an LDAP provider' do context 'when the provider exists' do diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb index d53a7d468e3..8b92088902b 100644 --- a/spec/lib/gitlab/ci/status/build/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb @@ -88,7 +88,7 @@ describe Gitlab::Ci::Status::Build::Factory do expect(status.icon).to eq 'status_failed' expect(status.favicon).to eq 'favicon_status_failed' expect(status.label).to eq 'failed' - expect(status.status_tooltip).to eq 'failed <br> (unknown failure)' + expect(status.status_tooltip).to eq 'failed - (unknown failure)' expect(status).to have_details expect(status).to have_action end diff --git a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb index bfaa508785e..af03d5a1308 100644 --- a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb +++ b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb @@ -76,7 +76,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do let(:status) { described_class.new(build_status) } it 'does override badge_tooltip' do - expect(status.badge_tooltip).to eq('failed <br> (unknown failure)') + expect(status.badge_tooltip).to eq('failed - (unknown failure)') end end @@ -87,7 +87,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do let(:status) { described_class.new(build_status) } it 'does override status_tooltip' do - expect(status.status_tooltip).to eq 'failed <br> (unknown failure) (allowed to fail)' + expect(status.status_tooltip).to eq 'failed - (unknown failure) (allowed to fail)' end end diff --git a/spec/lib/gitlab/ci/status/build/failed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_spec.rb index b6676b40fd3..e424270f7c5 100644 --- a/spec/lib/gitlab/ci/status/build/failed_spec.rb +++ b/spec/lib/gitlab/ci/status/build/failed_spec.rb @@ -52,7 +52,7 @@ describe Gitlab::Ci::Status::Build::Failed do let(:status) { Gitlab::Ci::Status::Failed.new(build, user) } it 'does override badge_tooltip' do - expect(subject.badge_tooltip).to eq 'failed <br> (script failure)' + expect(subject.badge_tooltip).to eq 'failed - (script failure)' end end @@ -61,7 +61,7 @@ describe Gitlab::Ci::Status::Build::Failed do let(:status) { Gitlab::Ci::Status::Failed.new(build, user) } it 'does override status_tooltip' do - expect(subject.status_tooltip).to eq 'failed <br> (script failure)' + expect(subject.status_tooltip).to eq 'failed - (script failure)' end end diff --git a/spec/lib/gitlab/ci/status/build/retried_spec.rb b/spec/lib/gitlab/ci/status/build/retried_spec.rb index ee9acaf1c21..76c2fb01e3f 100644 --- a/spec/lib/gitlab/ci/status/build/retried_spec.rb +++ b/spec/lib/gitlab/ci/status/build/retried_spec.rb @@ -66,7 +66,7 @@ describe Gitlab::Ci::Status::Build::Retried do let(:status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) } it 'does override status_tooltip' do - expect(subject.status_tooltip).to eq 'failed <br> (unknown failure) (retried)' + expect(subject.status_tooltip).to eq 'failed - (unknown failure) (retried)' end end diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb index 5b343920429..9095ffbfd52 100644 --- a/spec/lib/gitlab/conflict/file_spec.rb +++ b/spec/lib/gitlab/conflict/file_spec.rb @@ -69,10 +69,6 @@ describe Gitlab::Conflict::File do CGI.unescapeHTML(ActionView::Base.full_sanitizer.sanitize(html)).delete("\n") end - it 'modifies the existing lines' do - expect { conflict_file.highlight_lines! }.to change { conflict_file.lines.map(&:instance_variables) } - end - it 'is called implicitly when rich_text is accessed on a line' do expect(conflict_file).to receive(:highlight_lines!).once.and_call_original diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb index 7c9e8c8d04e..3c8cf9c56cc 100644 --- a/spec/lib/gitlab/diff/highlight_spec.rb +++ b/spec/lib/gitlab/diff/highlight_spec.rb @@ -24,19 +24,19 @@ describe Gitlab::Diff::Highlight do it 'highlights and marks unchanged lines' do code = %Q{ <span id="LC7" class="line" lang="ruby"> <span class="k">def</span> <span class="nf">popen</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span></span>\n} - expect(subject[2].text).to eq(code) + expect(subject[2].rich_text).to eq(code) end it 'highlights and marks removed lines' do code = %Q{-<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>\n} - expect(subject[4].text).to eq(code) + expect(subject[4].rich_text).to eq(code) end it 'highlights and marks added lines' do code = %Q{+<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="no"><span class="idiff left">RuntimeError</span></span><span class="p"><span class="idiff">,</span></span><span class="idiff right"> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n} - expect(subject[5].text).to eq(code) + expect(subject[5].rich_text).to eq(code) end end @@ -69,8 +69,8 @@ describe Gitlab::Diff::Highlight do it 'marks added lines' do code = %q{+ raise <span class="idiff left right">RuntimeError, </span>"System commands must be given as an array of strings"} - expect(subject[5].text).to eq(code) - expect(subject[5].text).to be_html_safe + expect(subject[5].rich_text).to eq(code) + expect(subject[5].rich_text).to be_html_safe end context 'when the inline diff marker has an invalid range' do diff --git a/spec/lib/gitlab/diff/line_spec.rb b/spec/lib/gitlab/diff/line_spec.rb new file mode 100644 index 00000000000..76d411973c7 --- /dev/null +++ b/spec/lib/gitlab/diff/line_spec.rb @@ -0,0 +1,22 @@ +describe Gitlab::Diff::Line do + describe '.init_from_hash' do + it 'round-trips correctly with to_hash' do + line = described_class.new('<input>', 'match', 0, 0, 1, + parent_file: double(:file), + line_code: double(:line_code), + rich_text: '<input>') + + expect(described_class.init_from_hash(line.to_hash).to_hash) + .to eq(line.to_hash) + end + end + + context "when setting rich text" do + it 'escapes any HTML special characters in the diff chunk header' do + subject = described_class.new("<input>", "", 0, 0, 0) + line = subject.as_json + + expect(line[:rich_text]).to eq("<input>") + end + end +end diff --git a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb index 3f7a12144d5..65a2e1cb5cb 100644 --- a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb @@ -92,7 +92,7 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach .with(issue) .and_return([user.id, true]) - expect(Gitlab::GithubImport) + expect(importer) .to receive(:insert_and_return_id) .with( { @@ -121,7 +121,7 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach .with(issue) .and_return([project.creator_id, false]) - expect(Gitlab::GithubImport) + expect(importer) .to receive(:insert_and_return_id) .with( { @@ -150,7 +150,7 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach .with(issue) .and_return([user.id, true]) - expect(Gitlab::GithubImport) + expect(importer) .to receive(:insert_and_return_id) .and_raise(ActiveRecord::InvalidForeignKey, 'invalid foreign key') @@ -185,7 +185,7 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach .and_return([user.id, true]) issue = build_stubbed(:issue, project: project) - allow(Gitlab::GithubImport) + allow(importer) .to receive(:insert_and_return_id) .and_return(issue.id) allow(project.issues).to receive(:find).with(issue.id).and_return(issue) diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb index 44c920043b4..25684ea9e2c 100644 --- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb @@ -80,7 +80,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi end it 'imports the pull request with the pull request author as the merge request author' do - expect(Gitlab::GithubImport) + expect(importer) .to receive(:insert_and_return_id) .with( { @@ -114,7 +114,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi it 'triggers internal_id functionality to track greatest iids' do mr = build_stubbed(:merge_request, source_project: project, target_project: project) - allow(Gitlab::GithubImport).to receive(:insert_and_return_id).and_return(mr.id) + allow(importer).to receive(:insert_and_return_id).and_return(mr.id) allow(project.merge_requests).to receive(:find).with(mr.id).and_return(mr) expect(mr).to receive(:ensure_target_project_iid!) @@ -135,7 +135,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi .with(pull_request) .and_return(user.id) - expect(Gitlab::GithubImport) + expect(importer) .to receive(:insert_and_return_id) .with( { @@ -181,7 +181,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi .to receive(:source_branch) .and_return('master') - expect(Gitlab::GithubImport) + expect(importer) .to receive(:insert_and_return_id) .with( { @@ -219,7 +219,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi .with(pull_request) .and_return(user.id) - expect(Gitlab::GithubImport) + expect(importer) .to receive(:insert_and_return_id) .and_raise(ActiveRecord::InvalidForeignKey, 'invalid foreign key') diff --git a/spec/lib/gitlab/github_import_spec.rb b/spec/lib/gitlab/github_import_spec.rb index 51414800e8c..496244c91bf 100644 --- a/spec/lib/gitlab/github_import_spec.rb +++ b/spec/lib/gitlab/github_import_spec.rb @@ -27,39 +27,6 @@ describe Gitlab::GithubImport do end end - describe '.insert_and_return_id' do - let(:attributes) { { iid: 1, title: 'foo' } } - let(:project) { create(:project) } - - context 'on PostgreSQL' do - it 'returns the ID returned by the query' do - expect(Gitlab::Database) - .to receive(:bulk_insert) - .with(Issue.table_name, [attributes], return_ids: true) - .and_return([10]) - - id = described_class.insert_and_return_id(attributes, project.issues) - - expect(id).to eq(10) - end - end - - context 'on MySQL' do - it 'uses a separate query to retrieve the ID' do - issue = create(:issue, project: project, iid: attributes[:iid]) - - expect(Gitlab::Database) - .to receive(:bulk_insert) - .with(Issue.table_name, [attributes], return_ids: true) - .and_return([]) - - id = described_class.insert_and_return_id(attributes, project.issues) - - expect(id).to eq(issue.id) - end - end - end - describe '.ghost_user_id', :clean_gitlab_redis_cache do it 'returns the ID of the ghost user' do expect(described_class.ghost_user_id).to eq(User.ghost.id) diff --git a/spec/lib/gitlab/import/database_helpers_spec.rb b/spec/lib/gitlab/import/database_helpers_spec.rb new file mode 100644 index 00000000000..e716155b7d5 --- /dev/null +++ b/spec/lib/gitlab/import/database_helpers_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Import::DatabaseHelpers do + let(:database_helper) do + Class.new do + include Gitlab::Import::DatabaseHelpers + end + end + + subject { database_helper.new } + + describe '.insert_and_return_id' do + let(:attributes) { { iid: 1, title: 'foo' } } + let(:project) { create(:project) } + + context 'on PostgreSQL' do + it 'returns the ID returned by the query' do + expect(Gitlab::Database) + .to receive(:bulk_insert) + .with(Issue.table_name, [attributes], return_ids: true) + .and_return([10]) + + id = subject.insert_and_return_id(attributes, project.issues) + + expect(id).to eq(10) + end + end + + context 'on MySQL' do + it 'uses a separate query to retrieve the ID' do + issue = create(:issue, project: project, iid: attributes[:iid]) + + expect(Gitlab::Database) + .to receive(:bulk_insert) + .with(Issue.table_name, [attributes], return_ids: true) + .and_return([]) + + id = subject.insert_and_return_id(attributes, project.issues) + + expect(id).to eq(issue.id) + end + end + end +end diff --git a/spec/lib/gitlab/import/merge_request_creator_spec.rb b/spec/lib/gitlab/import/merge_request_creator_spec.rb new file mode 100644 index 00000000000..7c73e9b39f7 --- /dev/null +++ b/spec/lib/gitlab/import/merge_request_creator_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Import::MergeRequestCreator do + let(:project) { create(:project, :repository) } + + subject { described_class.new(project) } + + describe '#execute' do + context 'merge request already exists' do + let(:merge_request) { create(:merge_request, target_project: project, source_project: project) } + let(:commits) { merge_request.merge_request_diffs.first.commits } + let(:attributes) { HashWithIndifferentAccess.new(merge_request.attributes.except("merge_params")) } + + it 'updates the data' do + commits_count = commits.count + merge_request.merge_request_diffs.destroy_all # rubocop: disable DestroyAll + + expect(merge_request.merge_request_diffs.count).to eq(0) + + subject.execute(attributes) + + expect(merge_request.reload.merge_request_diffs.count).to eq(1) + expect(merge_request.reload.merge_request_diffs.first.commits.count).to eq(commits_count) + end + end + + context 'new merge request' do + let(:merge_request) { build(:merge_request, target_project: project, source_project: project) } + let(:attributes) { HashWithIndifferentAccess.new(merge_request.attributes.except("merge_params")) } + + it 'creates a new merge request' do + attributes.delete(:id) + + expect { subject.execute(attributes) }.to change { MergeRequest.count }.by(1) + + new_mr = MergeRequest.last + expect(new_mr.merge_request_diffs.count).to eq(1) + end + end + end +end diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb index 9c3870a0af8..f799de18cd0 100644 --- a/spec/lib/gitlab/import_export/uploads_manager_spec.rb +++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb @@ -36,6 +36,38 @@ describe Gitlab::ImportExport::UploadsManager do expect(File).to exist(exported_file_path) end + + context 'with orphaned project upload files' do + let(:orphan_path) { File.join(FileUploader.absolute_base_dir(project), 'f93f088ddf492ffd950cf059002cbbb6', 'orphan.jpg') } + let(:exported_orphan_path) { "#{shared.export_path}/uploads/f93f088ddf492ffd950cf059002cbbb6/orphan.jpg" } + + before do + FileUtils.mkdir_p(File.dirname(orphan_path)) + FileUtils.touch(orphan_path) + end + + after do + File.delete(orphan_path) if File.exist?(orphan_path) + end + + it 'excludes orphaned upload files' do + manager.save + + expect(File).not_to exist(exported_orphan_path) + end + end + + context 'with an upload missing its file' do + before do + File.delete(upload.absolute_path) + end + + it 'does not cause errors' do + manager.save + + expect(shared.errors).to be_empty + end + end end context 'using object storage' do diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb index f788f8ee276..daf454665b0 100644 --- a/spec/lib/gitlab/middleware/multipart_spec.rb +++ b/spec/lib/gitlab/middleware/multipart_spec.rb @@ -75,6 +75,26 @@ describe Gitlab::Middleware::Multipart do it_behaves_like 'multipart upload files' end + it 'allows files in uploads/tmp directory' do + Dir.mktmpdir do |dir| + uploads_dir = File.join(dir, 'public/uploads/tmp') + FileUtils.mkdir_p(uploads_dir) + + allow(Rails).to receive(:root).and_return(dir) + allow(Dir).to receive(:tmpdir).and_return(File.join(Dir.tmpdir, 'tmpsubdir')) + + Tempfile.open('top-level', uploads_dir) do |tempfile| + env = post_env({ 'file' => tempfile.path }, { 'file.name' => original_filename, 'file.path' => tempfile.path }, Gitlab::Workhorse.secret, 'gitlab-workhorse') + + expect(app).to receive(:call) do |env| + expect(Rack::Request.new(env).params['file']).to be_a(::UploadedFile) + end + + middleware.call(env) + end + end + end + it 'allows symlinks for uploads dir' do Tempfile.open('two-levels') do |tempfile| symlinked_dir = '/some/dir/uploads' diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index 6f5f9938eca..624e2add860 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require 'spec_helper' describe Gitlab::UrlBlocker do @@ -82,6 +83,17 @@ describe Gitlab::UrlBlocker do expect(described_class).not_to be_blocked_url("http://#{ip}") end end + + it 'allows IPv4 link-local endpoints' do + expect(described_class).not_to be_blocked_url('http://169.254.169.254') + expect(described_class).not_to be_blocked_url('http://169.254.168.100') + end + + # This is blocked due to the hostname check: https://gitlab.com/gitlab-org/gitlab-ce/issues/50227 + it 'blocks IPv6 link-local endpoints' do + expect(described_class).to be_blocked_url('http://[::ffff:169.254.169.254]') + expect(described_class).to be_blocked_url('http://[::ffff:169.254.168.100]') + end end context 'false' do @@ -96,10 +108,21 @@ describe Gitlab::UrlBlocker do expect(described_class).to be_blocked_url("http://#{ip}", allow_local_network: false) end end + + it 'blocks IPv4 link-local endpoints' do + expect(described_class).to be_blocked_url('http://169.254.169.254', allow_local_network: false) + expect(described_class).to be_blocked_url('http://169.254.168.100', allow_local_network: false) + end + + it 'blocks IPv6 link-local endpoints' do + expect(described_class).to be_blocked_url('http://[::ffff:169.254.169.254]', allow_local_network: false) + expect(described_class).to be_blocked_url('http://[::ffff:169.254.168.100]', allow_local_network: false) + expect(described_class).to be_blocked_url('http://[FE80::C800:EFF:FE74:8]', allow_local_network: false) + end end def stub_domain_resolv(domain, ip) - allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([double(ip_address: ip, ipv4_private?: true)]) + allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([double(ip_address: ip, ipv4_private?: true, ipv6_link_local?: false)]) end def unstub_domain_resolv diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index a19b3c0ba66..de6dd2a9fea 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -56,6 +56,7 @@ describe Gitlab::UsageData do expect(count_data[:projects]).to eq(3) expect(count_data.keys).to match_array(%i( + assignee_lists boards ci_builds ci_internal_pipelines @@ -83,9 +84,11 @@ describe Gitlab::UsageData do groups issues keys + label_lists labels lfs_objects merge_requests + milestone_lists milestones notes projects diff --git a/spec/migrations/drop_duplicate_protected_tags_spec.rb b/spec/migrations/drop_duplicate_protected_tags_spec.rb new file mode 100644 index 00000000000..acfb6850722 --- /dev/null +++ b/spec/migrations/drop_duplicate_protected_tags_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' +require Rails.root.join('db', 'migrate', '20180711103851_drop_duplicate_protected_tags.rb') + +describe DropDuplicateProtectedTags, :migration do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:protected_tags) { table(:protected_tags) } + + before do + stub_const("#{described_class}::BATCH_SIZE", 2) + + namespaces.create(id: 1, name: 'gitlab-org', path: 'gitlab-org') + projects.create!(id: 1, namespace_id: 1, name: 'gitlab1', path: 'gitlab1') + projects.create!(id: 2, namespace_id: 1, name: 'gitlab2', path: 'gitlab2') + end + + it 'removes duplicated protected tags' do + protected_tags.create!(id: 1, project_id: 1, name: 'foo') + tag2 = protected_tags.create!(id: 2, project_id: 1, name: 'foo1') + protected_tags.create!(id: 3, project_id: 1, name: 'foo') + tag4 = protected_tags.create!(id: 4, project_id: 1, name: 'foo') + tag5 = protected_tags.create!(id: 5, project_id: 2, name: 'foo') + + migrate! + + expect(protected_tags.all.count).to eq 3 + expect(protected_tags.all.pluck(:id)).to contain_exactly(tag2.id, tag4.id, tag5.id) + end + + it 'does not remove unique protected tags' do + tag1 = protected_tags.create!(id: 1, project_id: 1, name: 'foo1') + tag2 = protected_tags.create!(id: 2, project_id: 1, name: 'foo2') + tag3 = protected_tags.create!(id: 3, project_id: 1, name: 'foo3') + + migrate! + + expect(protected_tags.all.count).to eq 3 + expect(protected_tags.all.pluck(:id)).to contain_exactly(tag1.id, tag2.id, tag3.id) + end +end diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb index a980cff28fb..69083bdc125 100644 --- a/spec/models/concerns/awardable_spec.rb +++ b/spec/models/concerns/awardable_spec.rb @@ -53,21 +53,14 @@ describe Awardable do issue.project.add_guest(user) end - it 'does not allow upvoting or downvoting your own issue' do - issue.update!(author: user) - - expect(issue.user_can_award?(user, AwardEmoji::DOWNVOTE_NAME)).to be_falsy - expect(issue.user_can_award?(user, AwardEmoji::UPVOTE_NAME)).to be_falsy - end - it 'is truthy when the user is allowed to award emoji' do - expect(issue.user_can_award?(user, AwardEmoji::UPVOTE_NAME)).to be_truthy + expect(issue.user_can_award?(user)).to be_truthy end it 'is falsy when the project is archived' do issue.project.update!(archived: true) - expect(issue.user_can_award?(user, AwardEmoji::UPVOTE_NAME)).to be_falsy + expect(issue.user_can_award?(user)).to be_falsy end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 84edfc3ff00..c21d85fb2a4 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -188,98 +188,6 @@ describe Issue do end end - describe '#closed_by_merge_requests' do - let(:project) { create(:project, :repository) } - let(:issue) { create(:issue, project: project)} - let(:closed_issue) { build(:issue, :closed, project: project)} - - let(:mr) do - opts = { - title: 'Awesome merge_request', - description: "Fixes #{issue.to_reference}", - source_branch: 'feature', - target_branch: 'master' - } - MergeRequests::CreateService.new(project, project.owner, opts).execute - end - - let(:closed_mr) do - opts = { - title: 'Awesome merge_request 2', - description: "Fixes #{issue.to_reference}", - source_branch: 'feature', - target_branch: 'master', - state: 'closed' - } - MergeRequests::CreateService.new(project, project.owner, opts).execute - end - - it 'returns the merge request to close this issue' do - expect(issue.closed_by_merge_requests(mr.author)).to eq([mr]) - end - - it "returns an empty array when the merge request is closed already" do - expect(issue.closed_by_merge_requests(closed_mr.author)).to eq([]) - end - - it "returns an empty array when the current issue is closed already" do - expect(closed_issue.closed_by_merge_requests(closed_issue.author)).to eq([]) - end - end - - describe '#referenced_merge_requests' do - let(:project) { create(:project, :public) } - let(:issue) do - create(:issue, description: merge_request.to_reference, project: project) - end - let!(:merge_request) do - create(:merge_request, - source_project: project, - source_branch: 'master', - target_branch: 'feature') - end - - it 'returns the referenced merge requests' do - mr2 = create(:merge_request, - source_project: project, - source_branch: 'feature', - target_branch: 'master') - - create(:note_on_issue, - noteable: issue, - note: mr2.to_reference, - project_id: project.id) - - expect(issue.referenced_merge_requests).to eq([merge_request, mr2]) - end - - it 'returns cross project referenced merge requests' do - other_project = create(:project, :public) - cross_project_merge_request = create(:merge_request, source_project: other_project) - create(:note_on_issue, - noteable: issue, - note: cross_project_merge_request.to_reference(issue.project), - project_id: issue.project.id) - - expect(issue.referenced_merge_requests).to eq([merge_request, cross_project_merge_request]) - end - - it 'excludes cross project references if the user cannot read cross project' do - user = create(:user) - allow(Ability).to receive(:allowed?).and_call_original - expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false } - - other_project = create(:project, :public) - cross_project_merge_request = create(:merge_request, source_project: other_project) - create(:note_on_issue, - noteable: issue, - note: cross_project_merge_request.to_reference(issue.project), - project_id: issue.project.id) - - expect(issue.referenced_merge_requests(user)).to eq([merge_request]) - end - end - describe '#can_move?' do let(:user) { create(:user) } let(:issue) { create(:issue) } @@ -365,7 +273,12 @@ describe Issue do source_project: subject.project, source_branch: "#{subject.iid}-branch" }) merge_request.create_cross_references!(user) - expect(subject.referenced_merge_requests(user)).not_to be_empty + + referenced_merge_requests = Issues::ReferencedMergeRequestsService + .new(subject.project, user) + .referenced_merge_requests(subject) + + expect(referenced_merge_requests).not_to be_empty expect(subject.related_branches(user)).to eq([subject.to_branch_name]) end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 8cb706b54b0..7cfffbde42f 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2989,6 +2989,7 @@ describe Project do # call. This makes testing a bit easier. allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) allow(project).to receive(:previous_changes).and_return('path' => ['foo']) + stub_feature_flags(skip_hashed_storage_upgrade: false) end it 'renames a repository' do @@ -3160,6 +3161,7 @@ describe Project do # call. This makes testing a bit easier. allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) allow(project).to receive(:previous_changes).and_return('path' => ['foo']) + stub_feature_flags(skip_hashed_storage_upgrade: false) end context 'migration to hashed storage' do diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index c2ef0435c8e..269d5deca20 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -220,6 +220,18 @@ describe RemoteMirror do end end + context '#ensure_remote!' do + let(:remote_mirror) { create(:project, :repository, :remote_mirror).remote_mirrors.first } + + it 'adds a remote multiple times with no errors' do + expect(remote_mirror.project.repository).to receive(:add_remote).with(remote_mirror.remote_name, remote_mirror.url).twice.and_call_original + + 2.times do + remote_mirror.ensure_remote! + end + end + end + context '#updated_since?' do let(:remote_mirror) { create(:project, :repository, :remote_mirror).remote_mirrors.first } let(:timestamp) { Time.now - 5.minutes } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 2859d5149ec..93898012d34 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2005,6 +2005,24 @@ describe Repository do File.delete(path) end + + context 'for multiple SHAs' do + it 'skips non-existent SHAs' do + repository.keep_around('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', sample_commit.id) + + expect(repository.kept_around?(sample_commit.id)).to be_truthy + end + + it 'skips already-kept-around SHAs' do + repository.keep_around(sample_commit.id) + + expect(repository.raw_repository).to receive(:write_ref).exactly(1).and_call_original + + repository.keep_around(sample_commit.id, another_sample_commit.id) + + expect(repository.kept_around?(another_sample_commit.id)).to be_truthy + end + end end describe '#update_ref' do diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 615fea11f26..5e583be457e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -17,7 +17,7 @@ describe GroupPolicy do let(:reporter_permissions) { [:admin_label] } - let(:developer_permissions) { [:admin_milestones] } + let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do [ @@ -31,6 +31,7 @@ describe GroupPolicy do :admin_namespace, :admin_group_member, :change_visibility_level, + :set_note_created_at, (Gitlab::Database.postgresql? ? :create_subgroup : nil) ].compact end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index dd3fa4e6a51..b7ec35d6ec5 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -64,6 +64,7 @@ describe ProjectPolicy do %i[ change_namespace change_visibility_level rename_project remove_project archive_project remove_fork_project destroy_merge_request destroy_issue + set_issue_iid set_issue_created_at set_note_created_at ] end diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb index 6dfaa3b72f7..547d95e0908 100644 --- a/spec/presenters/ci/build_presenter_spec.rb +++ b/spec/presenters/ci/build_presenter_spec.rb @@ -78,7 +78,7 @@ describe Ci::BuildPresenter do it 'returns the reason of failure' do status_title = presenter.status_title - expect(status_title).to eq('Failed <br> (unknown failure)') + expect(status_title).to eq('Failed - (unknown failure)') end end @@ -89,7 +89,7 @@ describe Ci::BuildPresenter do status_title = presenter.status_title expect(status_title).not_to include('(retried)') - expect(status_title).to eq('Failed <br> (unknown failure)') + expect(status_title).to eq('Failed - (unknown failure)') end end @@ -99,7 +99,7 @@ describe Ci::BuildPresenter do it 'returns the reason of failure' do status_title = presenter.status_title - expect(status_title).to eq('Failed <br> (unknown failure)') + expect(status_title).to eq('Failed - (unknown failure)') end end @@ -173,7 +173,7 @@ describe Ci::BuildPresenter do it 'returns the reason of failure' do tooltip = subject.tooltip_message - expect(tooltip).to eq("#{build.name} - failed <br> (script failure)") + expect(tooltip).to eq("#{build.name} - failed - (script failure)") end end @@ -183,7 +183,7 @@ describe Ci::BuildPresenter do it 'should include the reason of failure and the retried title' do tooltip = subject.tooltip_message - expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (retried)") + expect(tooltip).to eq("#{build.name} - failed - (script failure) (retried)") end end @@ -193,7 +193,7 @@ describe Ci::BuildPresenter do it 'should include the reason of failure' do tooltip = subject.tooltip_message - expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (allowed to fail)") + expect(tooltip).to eq("#{build.name} - failed - (script failure) (allowed to fail)") end end diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb index 0921fecb933..7f3f3ab0977 100644 --- a/spec/requests/api/award_emoji_spec.rb +++ b/spec/requests/api/award_emoji_spec.rb @@ -167,12 +167,6 @@ describe API::AwardEmoji do expect(response).to have_gitlab_http_status(401) end - it "returns a 404 error if the user authored issue" do - post api("/projects/#{project.id}/issues/#{issue2.id}/award_emoji", user), name: 'thumbsup' - - expect(response).to have_gitlab_http_status(404) - end - it "normalizes +1 as thumbsup award" do post api("/projects/#{project.id}/issues/#{issue.iid}/award_emoji", user), name: '+1' @@ -215,12 +209,6 @@ describe API::AwardEmoji do expect(json_response['user']['username']).to eq(user.username) end - it "it returns 404 error when user authored note" do - post api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{note2.id}/award_emoji", user), name: 'thumbsup' - - expect(response).to have_gitlab_http_status(404) - end - it "normalizes +1 as thumbsup award" do post api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{note.id}/award_emoji", user), name: '+1' diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 28ba00c7293..f64815feffa 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -1023,6 +1023,20 @@ describe API::Issues do end end + context 'by a group owner' do + let(:group) { create(:group) } + let(:group_project) { create(:project, :public, namespace: group) } + + it 'sets the internal ID on the new issue' do + group.add_owner(user2) + post api("/projects/#{group_project.id}/issues", user2), + title: 'new issue', iid: 9001 + + expect(response).to have_gitlab_http_status(201) + expect(json_response['iid']).to eq 9001 + end + end + context 'by another user' do it 'ignores the given internal ID' do post api("/projects/#{project.id}/issues", user2), @@ -1154,14 +1168,47 @@ describe API::Issues do end end - context 'when an admin or owner makes the request' do - it 'accepts the creation date to be set' do - creation_time = 2.weeks.ago - post api("/projects/#{project.id}/issues", user), - title: 'new issue', labels: 'label, label2', created_at: creation_time + context 'setting created_at' do + let(:creation_time) { 2.weeks.ago } + let(:params) { { title: 'new issue', labels: 'label, label2', created_at: creation_time } } - expect(response).to have_gitlab_http_status(201) - expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + context 'by an admin' do + it 'sets the creation time on the new issue' do + post api("/projects/#{project.id}/issues", admin), params + + expect(response).to have_gitlab_http_status(201) + expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + end + end + + context 'by a project owner' do + it 'sets the creation time on the new issue' do + post api("/projects/#{project.id}/issues", user), params + + expect(response).to have_gitlab_http_status(201) + expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + end + end + + context 'by a group owner' do + it 'sets the creation time on the new issue' do + group = create(:group) + group_project = create(:project, :public, namespace: group) + group.add_owner(user2) + post api("/projects/#{group_project.id}/issues", user2), params + + expect(response).to have_gitlab_http_status(201) + expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + end + end + + context 'by another user' do + it 'ignores the given creation time' do + post api("/projects/#{project.id}/issues", user2), params + + expect(response).to have_gitlab_http_status(201) + expect(Time.parse(json_response['created_at'])).not_to be_like_time(creation_time) + end end end diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index a4220f5b2be..b8a4a04a7e4 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -306,16 +306,16 @@ describe API::Labels do end it 'returns 400 for too long color code' do - post api("/projects/#{project.id}/labels", user), - name: 'Foo', + put api("/projects/#{project.id}/labels", user), + name: 'label1', color: '#FFAAFFFF' expect(response).to have_gitlab_http_status(400) expect(json_response['message']['color']).to eq(['must be a valid color code']) end it 'returns 400 for invalid priority' do - post api("/projects/#{project.id}/labels", user), - name: 'Foo', + put api("/projects/#{project.id}/labels", user), + name: 'label1', priority: 'foo' expect(response).to have_gitlab_http_status(400) diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb index 6c05c166bd6..62613aa5938 100644 --- a/spec/requests/api/project_milestones_spec.rb +++ b/spec/requests/api/project_milestones_spec.rb @@ -39,19 +39,6 @@ describe API::ProjectMilestones do expect(response).to have_gitlab_http_status(404) end - - it "rejects a member with reporter access from deleting a milestone" do - delete api("/projects/#{project.id}/milestones/#{milestone.id}", reporter) - - expect(response).to have_gitlab_http_status(403) - end - - it 'deletes the milestone when the user has developer access to the project' do - delete api("/projects/#{project.id}/milestones/#{milestone.id}", user) - - expect(project.milestones.find_by_id(milestone.id)).to be_nil - expect(response).to have_gitlab_http_status(204) - end end describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do diff --git a/spec/requests/api/protected_tags_spec.rb b/spec/requests/api/protected_tags_spec.rb new file mode 100644 index 00000000000..f4f3ef31bc3 --- /dev/null +++ b/spec/requests/api/protected_tags_spec.rb @@ -0,0 +1,202 @@ +require 'spec_helper' + +describe API::ProtectedTags do + let(:user) { create(:user) } + let!(:project) { create(:project, :repository) } + let(:project2) { create(:project, path: 'project2', namespace: user.namespace) } + let(:protected_name) { 'feature' } + let(:tag_name) { protected_name } + let!(:protected_tag) do + create(:protected_tag, project: project, name: protected_name) + end + + describe 'GET /projects/:id/protected_tags' do + let(:route) { "/projects/#{project.id}/protected_tags" } + + shared_examples_for 'protected tags' do + it 'returns the protected tags' do + get api(route, user), per_page: 100 + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + + protected_tag_names = json_response.map { |x| x['name'] } + expected_tags_names = project.protected_tags.map { |x| x['name'] } + expect(protected_tag_names).to match_array(expected_tags_names) + end + end + + context 'when authenticated as a maintainer' do + before do + project.add_maintainer(user) + end + + it_behaves_like 'protected tags' + end + + context 'when authenticated as a guest' do + before do + project.add_guest(user) + end + + it_behaves_like '403 response' do + let(:request) { get api(route, user) } + end + end + end + + describe 'GET /projects/:id/protected_tags/:tag' do + let(:route) { "/projects/#{project.id}/protected_tags/#{tag_name}" } + + shared_examples_for 'protected tag' do + it 'returns the protected tag' do + get api(route, user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response['name']).to eq(tag_name) + expect(json_response['create_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MAINTAINER) + end + + context 'when protected tag does not exist' do + let(:tag_name) { 'unknown' } + + it_behaves_like '404 response' do + let(:request) { get api(route, user) } + let(:message) { '404 Not found' } + end + end + end + + context 'when authenticated as a maintainer' do + before do + project.add_maintainer(user) + end + + it_behaves_like 'protected tag' + + context 'when protected tag contains a wildcard' do + let(:protected_name) { 'feature*' } + + it_behaves_like 'protected tag' + end + end + + context 'when authenticated as a guest' do + before do + project.add_guest(user) + end + + it_behaves_like '403 response' do + let(:request) { get api(route, user) } + end + end + end + + describe 'POST /projects/:id/protected_tags' do + let(:tag_name) { 'new_tag' } + + context 'when authenticated as a maintainer' do + before do + project.add_maintainer(user) + end + + it 'protects a single tag with maintainers can create tags' do + post api("/projects/#{project.id}/protected_tags", user), name: tag_name + + expect(response).to have_gitlab_http_status(201) + expect(json_response['name']).to eq(tag_name) + expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER) + end + + it 'protects a single tag with developers can create tags' do + post api("/projects/#{project.id}/protected_tags", user), + name: tag_name, create_access_level: 30 + + expect(response).to have_gitlab_http_status(201) + expect(json_response['name']).to eq(tag_name) + expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER) + end + + it 'protects a single tag with no one can create tags' do + post api("/projects/#{project.id}/protected_tags", user), + name: tag_name, create_access_level: 0 + + expect(response).to have_gitlab_http_status(201) + expect(json_response['name']).to eq(tag_name) + expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS) + end + + it 'returns a 422 error if the same tag is protected twice' do + post api("/projects/#{project.id}/protected_tags", user), name: protected_name + + expect(response).to have_gitlab_http_status(422) + expect(json_response['message'][0]).to eq('Name has already been taken') + end + + it 'returns 201 if the same tag is proteted on different projects' do + post api("/projects/#{project.id}/protected_tags", user), name: protected_name + post api("/projects/#{project2.id}/protected_tags", user), name: protected_name + + expect(response).to have_gitlab_http_status(201) + expect(json_response['name']).to eq(protected_name) + end + + context 'when tag has a wildcard in its name' do + let(:tag_name) { 'feature/*' } + + it 'protects multiple tags with a wildcard in the name' do + post api("/projects/#{project.id}/protected_tags", user), name: tag_name + + expect(response).to have_gitlab_http_status(201) + expect(json_response['name']).to eq(tag_name) + expect(json_response['create_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER) + end + end + end + + context 'when authenticated as a guest' do + before do + project.add_guest(user) + end + + it 'returns a 403 error if guest' do + post api("/projects/#{project.id}/protected_tags/", user), name: tag_name + + expect(response).to have_gitlab_http_status(403) + end + end + end + + describe 'DELETE /projects/:id/protected_tags/unprotect/:tag' do + before do + project.add_maintainer(user) + end + + it 'unprotects a single tag' do + delete api("/projects/#{project.id}/protected_tags/#{tag_name}", user) + + expect(response).to have_gitlab_http_status(204) + end + + it_behaves_like '412 response' do + let(:request) { api("/projects/#{project.id}/protected_tags/#{tag_name}", user) } + end + + it "returns 404 if tag does not exist" do + delete api("/projects/#{project.id}/protected_tags/barfoo", user) + + expect(response).to have_gitlab_http_status(404) + end + + context 'when tag has a wildcard in its name' do + let(:protected_name) { 'feature*' } + + it 'unprotects a wildcard tag' do + delete api("/projects/#{project.id}/protected_tags/#{tag_name}", user) + + expect(response).to have_gitlab_http_status(204) + end + end + end +end diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 98df5f787f7..77baaef7afd 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -103,11 +103,11 @@ describe Admin::HooksController, "routing" do end end -# admin_hook_hook_log_retry GET /admin/hooks/:hook_id/hook_logs/:id/retry(.:format) admin/hook_logs#retry +# admin_hook_hook_log_retry POST /admin/hooks/:hook_id/hook_logs/:id/retry(.:format) admin/hook_logs#retry # admin_hook_hook_log GET /admin/hooks/:hook_id/hook_logs/:id(.:format) admin/hook_logs#show describe Admin::HookLogsController, 'routing' do it 'to #retry' do - expect(get('/admin/hooks/1/hook_logs/1/retry')).to route_to('admin/hook_logs#retry', hook_id: '1', id: '1') + expect(post('/admin/hooks/1/hook_logs/1/retry')).to route_to('admin/hook_logs#retry', hook_id: '1', id: '1') end it 'to #show' do diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 70a7707826e..5abc6d81958 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -381,7 +381,7 @@ describe 'project routing' do end end - # test_project_hook GET /:project_id/hooks/:id/test(.:format) hooks#test + # test_project_hook POST /:project_id/hooks/:id/test(.:format) hooks#test # project_hooks GET /:project_id/hooks(.:format) hooks#index # POST /:project_id/hooks(.:format) hooks#create # edit_project_hook GET /:project_id/hooks/:id/edit(.:format) hooks#edit @@ -398,11 +398,11 @@ describe 'project routing' do end end - # retry_namespace_project_hook_hook_log GET /:project_id/hooks/:hook_id/hook_logs/:id/retry(.:format) projects/hook_logs#retry + # retry_namespace_project_hook_hook_log POST /:project_id/hooks/:hook_id/hook_logs/:id/retry(.:format) projects/hook_logs#retry # namespace_project_hook_hook_log GET /:project_id/hooks/:hook_id/hook_logs/:id(.:format) projects/hook_logs#show describe Projects::HookLogsController, 'routing' do it 'to #retry' do - expect(get('/gitlab/gitlabhq/hooks/1/hook_logs/1/retry')).to route_to('projects/hook_logs#retry', namespace_id: 'gitlab', project_id: 'gitlabhq', hook_id: '1', id: '1') + expect(post('/gitlab/gitlabhq/hooks/1/hook_logs/1/retry')).to route_to('projects/hook_logs#retry', namespace_id: 'gitlab', project_id: 'gitlabhq', hook_id: '1', id: '1') end it 'to #show' do diff --git a/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb new file mode 100644 index 00000000000..7bd50866577 --- /dev/null +++ b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../rubocop/cop/ruby_interpolation_in_translation' + +# Disabling interpolation check as we deliberately want to have #{} in strings. +# rubocop:disable Lint/InterpolationCheck +describe RuboCop::Cop::RubyInterpolationInTranslation do + subject(:cop) { described_class.new } + + it 'does not add an offence for a regular messages' do + inspect_source('_("Hello world")') + + expect(cop.offenses).to be_empty + end + + it 'adds the correct offence when using interpolation in a string' do + inspect_source('_("Hello #{world}")') + + offense = cop.offenses.first + + expect(offense.location.source).to eq('#{world}') + expect(offense.message).to eq('Don\'t use ruby interpolation #{} inside translated strings, instead use %{}') + end + + it 'detects when using a ruby interpolation in the first argument of a pluralized string' do + inspect_source('n_("Hello #{world}", "Hello world")') + + expect(cop.offenses).not_to be_empty + end + + it 'detects when using a ruby interpolation in the second argument of a pluralized string' do + inspect_source('n_("Hello world", "Hello #{world}")') + + expect(cop.offenses).not_to be_empty + end + + it 'detects when using interpolation in a namespaced translation' do + inspect_source('s_("Hello|#{world}")') + + expect(cop.offenses).not_to be_empty + end + + it 'does not add an offence for messages defined over multiple lines' do + source = <<~SRC + _("Hello "\ + "world ") + SRC + + inspect_source(source) + expect(cop.offenses).to be_empty + end + + it 'adds an offence for violations in a message defined over multiple lines' do + source = <<~SRC + _("Hello "\ + "\#{world} ") + SRC + + inspect_source(source) + expect(cop.offenses).not_to be_empty + end +end +# rubocop:enable Lint/InterpolationCheck diff --git a/spec/serializers/build_serializer_spec.rb b/spec/serializers/build_serializer_spec.rb index 52459cd369d..302ef147eb2 100644 --- a/spec/serializers/build_serializer_spec.rb +++ b/spec/serializers/build_serializer_spec.rb @@ -37,7 +37,7 @@ describe BuildSerializer do it 'serializes only status' do expect(subject[:text]).to eq(status.text) expect(subject[:label]).to eq('failed') - expect(subject[:tooltip]).to eq('failed <br> (unknown failure)') + expect(subject[:tooltip]).to eq('failed - (unknown failure)') expect(subject[:icon]).to eq(status.icon) expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") end diff --git a/spec/serializers/job_entity_spec.rb b/spec/serializers/job_entity_spec.rb index a5581a34517..8e1ca3f308d 100644 --- a/spec/serializers/job_entity_spec.rb +++ b/spec/serializers/job_entity_spec.rb @@ -142,7 +142,7 @@ describe JobEntity do end it 'should indicate the failure reason on tooltip' do - expect(subject[:status][:tooltip]).to eq('failed <br> (API failure)') + expect(subject[:status][:tooltip]).to eq('failed - (API failure)') end it 'should include a callout message with a verbose output' do @@ -166,7 +166,7 @@ describe JobEntity do end it 'should indicate the failure reason on tooltip' do - expect(subject[:status][:tooltip]).to eq('failed <br> (API failure) (allowed to fail)') + expect(subject[:status][:tooltip]).to eq('failed - (API failure) (allowed to fail)') end it 'should include a callout message with a verbose output' do diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 3b1a302e217..d4528256640 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -204,16 +204,37 @@ describe GitPushService, services: true do end describe "Push Event" do - let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) } - let(:event) { Event.find_by_action(Event::PUSHED) } + context "with an existing branch" do + let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) } + let(:event) { Event.find_by_action(Event::PUSHED) } - it { expect(event).to be_an_instance_of(PushEvent) } - it { expect(event.project).to eq(project) } - it { expect(event.action).to eq(Event::PUSHED) } - it { expect(event.push_event_payload).to be_an_instance_of(PushEventPayload) } - it { expect(event.push_event_payload.commit_from).to eq(oldrev) } - it { expect(event.push_event_payload.commit_to).to eq(newrev) } - it { expect(event.push_event_payload.ref).to eq('master') } + it 'generates a push event with one commit' do + expect(event).to be_an_instance_of(PushEvent) + expect(event.project).to eq(project) + expect(event.action).to eq(Event::PUSHED) + expect(event.push_event_payload).to be_an_instance_of(PushEventPayload) + expect(event.push_event_payload.commit_from).to eq(oldrev) + expect(event.push_event_payload.commit_to).to eq(newrev) + expect(event.push_event_payload.ref).to eq('master') + expect(event.push_event_payload.commit_count).to eq(1) + end + end + + context "with a new branch" do + let!(:new_branch_data) { push_data_from_service(project, user, Gitlab::Git::BLANK_SHA, newrev, ref) } + let(:event) { Event.find_by_action(Event::PUSHED) } + + it 'generates a push event with more than one commit' do + expect(event).to be_an_instance_of(PushEvent) + expect(event.project).to eq(project) + expect(event.action).to eq(Event::PUSHED) + expect(event.push_event_payload).to be_an_instance_of(PushEventPayload) + expect(event.push_event_payload.commit_from).to be_nil + expect(event.push_event_payload.commit_to).to eq(newrev) + expect(event.push_event_payload.ref).to eq('master') + expect(event.push_event_payload.commit_count).to be > 1 + end + end context "Updates merge requests" do it "when pushing a new branch for the first time" do @@ -224,6 +245,7 @@ describe GitPushService, services: true do end describe 'system hooks' do + let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) } let(:system_hooks_service) { SystemHooksService.new } it "sends a system hook after pushing a branch" do diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb index d80d0f5a8a8..97a88b5d697 100644 --- a/spec/services/groups/destroy_service_spec.rb +++ b/spec/services/groups/destroy_service_spec.rb @@ -35,6 +35,14 @@ describe Groups::DestroyService do it { expect(NotificationSetting.unscoped.all).not_to include(notification_setting) } end + context 'site statistics' do + it 'doesnt trigger project deletion hooks twice' do + expect_any_instance_of(Project).to receive(:untrack_site_statistics).once + + destroy_group(group, user, async) + end + end + context 'mattermost team' do let!(:chat_team) { create(:chat_team, namespace: group) } diff --git a/spec/services/issues/fetch_referenced_merge_requests_service_spec.rb b/spec/services/issues/fetch_referenced_merge_requests_service_spec.rb deleted file mode 100644 index 4e58179f45f..00000000000 --- a/spec/services/issues/fetch_referenced_merge_requests_service_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'spec_helper.rb' - -describe Issues::FetchReferencedMergeRequestsService do - let(:project) { create(:project) } - let(:issue) { create(:issue, project: project) } - let(:other_project) { create(:project) } - - let(:mr) { create(:merge_request, source_project: project, target_project: project, id: 2)} - let(:other_mr) { create(:merge_request, source_project: other_project, target_project: other_project, id: 1)} - - let(:user) { create(:user) } - let(:service) { described_class.new(project, user) } - - context 'with mentioned merge requests' do - it 'returns a list of sorted merge requests' do - allow(issue).to receive(:referenced_merge_requests).with(user).and_return([other_mr, mr]) - - mrs, closed_by_mrs = service.execute(issue) - - expect(mrs).to match_array([mr, other_mr]) - expect(closed_by_mrs).to match_array([]) - end - end - - context 'with closed-by merge requests' do - it 'returns a list of sorted merge requests' do - allow(issue).to receive(:closed_by_merge_requests).with(user).and_return([other_mr, mr]) - - mrs, closed_by_mrs = service.execute(issue) - - expect(mrs).to match_array([]) - expect(closed_by_mrs).to match_array([mr, other_mr]) - end - end -end diff --git a/spec/services/issues/referenced_merge_requests_service_spec.rb b/spec/services/issues/referenced_merge_requests_service_spec.rb new file mode 100644 index 00000000000..61d1612829f --- /dev/null +++ b/spec/services/issues/referenced_merge_requests_service_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +require 'spec_helper.rb' + +describe Issues::ReferencedMergeRequestsService do + def create_referencing_mr(attributes = {}) + create(:merge_request, attributes).tap do |merge_request| + create(:note, :system, project: project, noteable: issue, author: user, note: merge_request.to_reference(full: true)) + end + end + + def create_closing_mr(attributes = {}) + create_referencing_mr(attributes).tap do |merge_request| + create(:merge_requests_closing_issues, issue: issue, merge_request: merge_request) + end + end + + set(:user) { create(:user) } + set(:project) { create(:project, :public, :repository) } + set(:other_project) { create(:project, :public, :repository) } + set(:issue) { create(:issue, author: user, project: project) } + + set(:closing_mr) { create_closing_mr(source_project: project) } + set(:closing_mr_other_project) { create_closing_mr(source_project: other_project) } + + set(:referencing_mr) { create_referencing_mr(source_project: project, source_branch: 'csv') } + set(:referencing_mr_other_project) { create_referencing_mr(source_project: other_project, source_branch: 'csv') } + + let(:service) { described_class.new(project, user) } + + describe '#execute' do + it 'returns a list of sorted merge requests' do + mrs, closed_by_mrs = service.execute(issue) + + expect(mrs).to eq([closing_mr, referencing_mr, closing_mr_other_project, referencing_mr_other_project]) + expect(closed_by_mrs).to eq([closing_mr, closing_mr_other_project]) + end + + context 'performance' do + it 'does not run extra queries when extra namespaces are included', :use_clean_rails_memory_store_caching do + service.execute(issue) # warm cache + control_count = ActiveRecord::QueryRecorder.new { service.execute(issue) }.count + + third_project = create(:project, :public) + create_closing_mr(source_project: third_project) + service.execute(issue) # warm cache + + expect { service.execute(issue) }.not_to exceed_query_limit(control_count) + end + + it 'preloads the head pipeline for each merge request, and its routes' do + # Hack to ensure no data is preserved on issue before starting the spec, + # to avoid false negatives + reloaded_issue = Issue.find(issue.id) + + pipeline_routes = lambda do |merge_requests| + merge_requests.map { |mr| mr.head_pipeline&.project&.full_path } + end + + closing_mr_other_project.update!(head_pipeline: create(:ci_pipeline)) + control_count = ActiveRecord::QueryRecorder.new { service.execute(reloaded_issue).each(&pipeline_routes) } + + closing_mr.update!(head_pipeline: create(:ci_pipeline)) + + expect { service.execute(issue).each(&pipeline_routes) } + .not_to exceed_query_limit(control_count) + end + + it 'only loads issue notes once' do + expect(issue).to receive(:notes).once.and_call_original + + service.execute(issue) + end + end + end + + describe '#referenced_merge_requests' do + it 'returns the referenced merge requests' do + expect(service.referenced_merge_requests(issue)).to match_array([ + closing_mr, + closing_mr_other_project, + referencing_mr, + referencing_mr_other_project + ]) + end + + it 'excludes cross project references if the user cannot read cross project' do + allow(Ability).to receive(:allowed?).and_call_original + expect(Ability).to receive(:allowed?).with(user, :read_cross_project).at_least(:once).and_return(false) + + expect(service.referenced_merge_requests(issue)).not_to include(closing_mr_other_project) + expect(service.referenced_merge_requests(issue)).not_to include(referencing_mr_other_project) + end + + context 'performance' do + it 'does not run a query for each note author', :use_clean_rails_memory_store_caching do + service.referenced_merge_requests(issue) # warm cache + control_count = ActiveRecord::QueryRecorder.new { service.referenced_merge_requests(issue) }.count + + create(:note, project: project, noteable: issue, author: create(:user)) + service.referenced_merge_requests(issue) # warm cache + + expect { service.referenced_merge_requests(issue) }.not_to exceed_query_limit(control_count) + end + end + end + + describe '#closed_by_merge_requests' do + let(:closed_issue) { build(:issue, :closed, project: project)} + + it 'returns the open merge requests that close this issue' do + create_closing_mr(source_project: project, state: 'closed') + + expect(service.closed_by_merge_requests(issue)).to match_array([closing_mr, closing_mr_other_project]) + end + + it 'returns an empty array when the current issue is closed already' do + expect(service.closed_by_merge_requests(closed_issue)).to eq([]) + end + + context 'performance' do + it 'does not run a query for each note author', :use_clean_rails_memory_store_caching do + service.closed_by_merge_requests(issue) # warm cache + control_count = ActiveRecord::QueryRecorder.new { service.closed_by_merge_requests(issue) }.count + + create(:note, :system, project: project, noteable: issue, author: create(:user)) + service.closed_by_merge_requests(issue) # warm cache + + expect { service.closed_by_merge_requests(issue) }.not_to exceed_query_limit(control_count) + end + end + end +end diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index 6aed481939e..0ced5d1b6d6 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -169,6 +169,10 @@ describe MergeRequests::BuildService do end end + it 'uses the title of the commit as the title of the merge request' do + expect(merge_request.title).to eq('Initial commit') + end + it 'appends the closing description' do expected_description = [commit_description, closing_message].compact.join("\n\n") diff --git a/spec/services/milestones/destroy_service_spec.rb b/spec/services/milestones/destroy_service_spec.rb index 6f3612501f4..8680e428517 100644 --- a/spec/services/milestones/destroy_service_spec.rb +++ b/spec/services/milestones/destroy_service_spec.rb @@ -4,8 +4,6 @@ describe Milestones::DestroyService do let(:user) { create(:user) } let(:project) { create(:project) } let(:milestone) { create(:milestone, title: 'Milestone v1.0', project: project) } - let!(:issue) { create(:issue, project: project, milestone: milestone) } - let!(:merge_request) { create(:merge_request, source_project: project, milestone: milestone) } before do project.add_maintainer(user) @@ -23,12 +21,23 @@ describe Milestones::DestroyService do end it 'deletes milestone id from issuables' do + issue = create(:issue, project: project, milestone: milestone) + merge_request = create(:merge_request, source_project: project, milestone: milestone) + service.execute(milestone) expect(issue.reload.milestone).to be_nil expect(merge_request.reload.milestone).to be_nil end + it 'logs destroy event' do + service.execute(milestone) + + event = Event.where(project_id: milestone.project_id, target_type: 'Milestone') + + expect(event.count).to eq(1) + end + context 'group milestones' do let(:group) { create(:group) } let(:group_milestone) { create(:milestone, group: group) } @@ -38,13 +47,20 @@ describe Milestones::DestroyService do group.add_developer(user) end - it { expect(service.execute(group_milestone)).to be_nil } + it { expect(service.execute(group_milestone)).to eq(group_milestone) } - it 'does not update milestone issuables' do - expect(MergeRequests::UpdateService).not_to receive(:new) - expect(Issues::UpdateService).not_to receive(:new) + it 'deletes milestone id from issuables' do + issue = create(:issue, project: project, milestone: group_milestone) + merge_request = create(:merge_request, source_project: project, milestone: group_milestone) service.execute(group_milestone) + + expect(issue.reload.milestone).to be_nil + expect(merge_request.reload.milestone).to be_nil + end + + it 'does not log destroy event' do + expect { service.execute(group_milestone) }.not_to change { Event.count } end end end diff --git a/spec/services/projects/update_remote_mirror_service_spec.rb b/spec/services/projects/update_remote_mirror_service_spec.rb index 5c2e79ff9af..96e8a80b334 100644 --- a/spec/services/projects/update_remote_mirror_service_spec.rb +++ b/spec/services/projects/update_remote_mirror_service_spec.rb @@ -18,6 +18,7 @@ describe Projects::UpdateRemoteMirrorService do end it "fetches the remote repository" do + expect(remote_mirror).to receive(:ensure_remote!).and_call_original expect(repository).to receive(:fetch_remote).with(remote_mirror.remote_name, no_tags: true) do sync_remote(repository, remote_mirror.remote_name, local_branch_names) end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 9572b4110d5..695b9980548 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -249,9 +249,20 @@ describe Projects::UpdateService do expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk') end - context 'when hashed storage enabled' do + it 'renames the project without upgrading it' do + result = update_project(project, admin, path: 'new-path') + + expect(result).not_to include(status: :error) + expect(project).to be_valid + expect(project.errors).to be_empty + expect(project.disk_path).to include('new-path') + expect(project.reload.hashed_storage?(:repository)).to be_falsey + end + + context 'when hashed storage is enabled' do before do stub_application_setting(hashed_storage_enabled: true) + stub_feature_flags(skip_hashed_storage_upgrade: false) end it 'migrates project to a hashed storage instead of renaming the repo to another legacy name' do @@ -262,6 +273,22 @@ describe Projects::UpdateService do expect(project.errors).to be_empty expect(project.reload.hashed_storage?(:repository)).to be_truthy end + + context 'when skip_hashed_storage_upgrade feature flag is enabled' do + before do + stub_feature_flags(skip_hashed_storage_upgrade: true) + end + + it 'renames the project without upgrading it' do + result = update_project(project, admin, path: 'new-path') + + expect(result).not_to include(status: :error) + expect(project).to be_valid + expect(project.errors).to be_empty + expect(project.disk_path).to include('new-path') + expect(project.reload.hashed_storage?(:repository)).to be_falsey + end + end end end diff --git a/spec/support/api/milestones_shared_examples.rb b/spec/support/api/milestones_shared_examples.rb index afd6448aa26..3bebb7aae90 100644 --- a/spec/support/api/milestones_shared_examples.rb +++ b/spec/support/api/milestones_shared_examples.rb @@ -196,6 +196,24 @@ shared_examples_for 'group and project milestones' do |route_definition| end end + describe "DELETE #{route_definition}/:milestone_id" do + it "rejects a member with reporter access from deleting a milestone" do + reporter = create(:user) + milestone.parent.add_reporter(reporter) + + delete api(resource_route, reporter) + + expect(response).to have_gitlab_http_status(403) + end + + it 'deletes the milestone when the user has developer access to the project' do + delete api(resource_route, user) + + expect(project.milestones.find_by_id(milestone.id)).to be_nil + expect(response).to have_gitlab_http_status(204) + end + end + describe "GET #{route_definition}/:milestone_id/issues" do let(:issues_route) { "#{route}/#{milestone.id}/issues" } diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index c228bd2393b..e0fceae88de 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -65,7 +65,9 @@ module CycleAnalyticsHelpers end def merge_merge_requests_closing_issue(user, project, issue) - merge_requests = issue.closed_by_merge_requests(user) + merge_requests = Issues::ReferencedMergeRequestsService + .new(project, user) + .closed_by_merge_requests(issue) merge_requests.each { |merge_request| MergeRequests::MergeService.new(project, user).execute(merge_request) } end diff --git a/spec/support/helpers/ldap_helpers.rb b/spec/support/helpers/ldap_helpers.rb index b90bbc4b106..66ca5d7f0a3 100644 --- a/spec/support/helpers/ldap_helpers.rb +++ b/spec/support/helpers/ldap_helpers.rb @@ -37,6 +37,23 @@ module LdapHelpers .to receive(:find_by_uid).with(uid, any_args).and_return(return_value) end + def stub_ldap_person_find_by_dn(entry, provider = 'ldapmain') + person = ::Gitlab::Auth::LDAP::Person.new(entry, provider) if entry.present? + + allow(::Gitlab::Auth::LDAP::Person) + .to receive(:find_by_dn) + .and_return(person) + end + + def stub_ldap_person_find_by_email(email, entry, provider = 'ldapmain') + person = ::Gitlab::Auth::LDAP::Person.new(entry, provider) if entry.present? + + allow(::Gitlab::Auth::LDAP::Person) + .to receive(:find_by_email) + .with(email, anything) + .and_return(person) + end + # Create a simple LDAP user entry. def ldap_user_entry(uid) entry = Net::LDAP::Entry.new diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb index 1823099dd9c..8475f91799b 100644 --- a/spec/support/helpers/stub_configuration.rb +++ b/spec/support/helpers/stub_configuration.rb @@ -68,6 +68,10 @@ module StubConfiguration allow(Gitlab.config.repositories).to receive(:storages).and_return(Settingslogic.new(messages)) end + def stub_kerberos_setting(messages) + allow(Gitlab.config.kerberos).to receive_messages(to_settings(messages)) + end + private # Modifies stubbed messages to also stub possible predicate versions diff --git a/spec/support/helpers/stub_feature_flags.rb b/spec/support/helpers/stub_feature_flags.rb index b96338bf548..c54a871b157 100644 --- a/spec/support/helpers/stub_feature_flags.rb +++ b/spec/support/helpers/stub_feature_flags.rb @@ -1,4 +1,7 @@ module StubFeatureFlags + # Stub Feature flags with `flag_name: true/false` + # + # @param [Hash] features where key is feature name and value is boolean whether enabled or not def stub_feature_flags(features) features.each do |feature_name, enabled| allow(Feature).to receive(:enabled?).with(feature_name) { enabled } diff --git a/spec/support/shared_examples/requests/api/notes.rb b/spec/support/shared_examples/requests/api/notes.rb index 1b563021244..0e20dfe0725 100644 --- a/spec/support/shared_examples/requests/api/notes.rb +++ b/spec/support/shared_examples/requests/api/notes.rb @@ -111,17 +111,79 @@ shared_examples 'noteable API' do |parent_type, noteable_type, id_name| post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), body: 'hi!' end - context 'when an admin or owner makes the request' do - it 'accepts the creation date to be set' do - creation_time = 2.weeks.ago - post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), - body: 'hi!', created_at: creation_time + context 'setting created_at' do + let(:creation_time) { 2.weeks.ago } + let(:params) { { body: 'hi!', created_at: creation_time } } + + context 'by an admin' do + it 'sets the creation time on the new note' do + admin = create(:admin) + post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", admin), params + + expect(response).to have_gitlab_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(admin.username) + expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time) + end + end - expect(response).to have_gitlab_http_status(201) - expect(json_response['body']).to eq('hi!') - expect(json_response['author']['username']).to eq(user.username) - expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) - expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time) + if parent_type == 'projects' + context 'by a project owner' do + it 'sets the creation time on the new note' do + post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params + + expect(response).to have_gitlab_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) + expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time) + end + end + + context 'by a group owner' do + it 'sets the creation time on the new note' do + user2 = create(:user) + group = create(:group) + group.add_owner(user2) + parent.update!(namespace: group) + user2.refresh_authorized_projects + + post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user2), params + + expect(response).to have_gitlab_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user2.username) + expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time) + end + end + elsif parent_type == 'groups' + context 'by a group owner' do + it 'sets the creation time on the new note' do + post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params + + expect(response).to have_gitlab_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) + expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time) + end + end + end + + context 'by another user' do + it 'ignores the given creation time' do + user2 = create(:user) + parent.add_developer(user2) + post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user2), params + + expect(response).to have_gitlab_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user2.username) + expect(Time.parse(json_response['created_at'])).not_to be_like_time(creation_time) + expect(Time.parse(json_response['updated_at'])).not_to be_like_time(creation_time) + end end end diff --git a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb index b1c6565c08a..a7628548de6 100644 --- a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb +++ b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb @@ -20,6 +20,7 @@ describe 'projects/merge_requests/_commits.html.haml' do assign(:merge_request, merge_request) assign(:commits, merge_request.commits) + assign(:hidden_commit_count, 0) end it 'shows commits from source project' do @@ -30,4 +31,16 @@ describe 'projects/merge_requests/_commits.html.haml' do expect(rendered).to have_link(href: href) end + + context 'when there are hidden commits' do + before do + assign(:hidden_commit_count, 1) + end + + it 'shows notice about omitted commits' do + render + + expect(rendered).to match(/1 additional commit has been omitted to prevent performance issues/) + end + end end diff --git a/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb b/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb index 9ab105c3238..8befae39d3a 100644 --- a/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb +++ b/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb @@ -9,6 +9,8 @@ describe 'projects/merge_requests/creations/_new_submit.html.haml' do assign(:merge_request, merge_request) assign(:commits, merge_request.commits) + assign(:hidden_commit_count, 0) + assign(:total_commit_count, merge_request.commits.count) assign(:project, merge_request.target_project) allow(view).to receive(:can?).and_return(true) @@ -29,4 +31,17 @@ describe 'projects/merge_requests/creations/_new_submit.html.haml' do expect(rendered).not_to have_text('Builds') end end + + context 'when there are hidden commits' do + before do + assign(:pipelines, Ci::Pipeline.none) + assign(:hidden_commit_count, 2) + end + + it 'shows notice about omitted commits' do + render + + expect(rendered).to match(/2 additional commits have been omitted to prevent performance issues/) + end + end end |