diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index afe9da084957777e43fa4ec3868ea9c5144565c3..c7ef75d1c32d919d3feb5adeac587b3c2ec38854 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -447,9 +447,8 @@ danger-review: - retry gem install danger --no-ri --no-rdoc cache: {} only: - refs: - - branches@gitlab-org/gitlab-ce - - branches@gitlab-org/gitlab-ee + variables: + - $DANGER_GITLAB_API_TOKEN except: refs: - master diff --git a/CHANGELOG.md b/CHANGELOG.md index 76a016b233c72549f54bc9a8634bafffdd2f9a6c..7cc047c40ed9fdae828b637929e26bcc2dd79dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 11.1.1 (2018-07-23) + +### Fixed (2 changes) + +- Add missing Gitaly branch_update nil checks. !20711 +- Fix filename for accelerated uploads. + +### Added (1 change) + +- Add uploader support to Import/Export uploads. !20484 + + ## 11.1.0 (2018-07-22) ### Security (6 changes) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6056e18595dff4f1cc47d06826381e8b5e4f8103..ad8022e972fd95448c00123e847198a70bea04da 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -133,7 +133,7 @@ Most issues will have labels for at least one of the following: - Type: ~"feature proposal", ~bug, ~customer, etc. - Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc. -- Team: ~"CI/CD", ~Plan, ~Quality, ~Platform, etc. +- Team: ~"CI/CD", ~Plan, ~Manage, ~Quality, etc. - Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release" - Priority: ~P1, ~P2, ~P3, ~P4 - Severity: ~S1, ~S2, ~S3, ~S4 @@ -192,9 +192,9 @@ The current team labels are: - ~Documentation - ~Geo - ~Gitaly +- ~Manage - ~Monitoring - ~Plan -- ~Platform - ~Quality - ~Release - ~"Security Products" @@ -376,8 +376,14 @@ on those issues. Please select someone with relevant experience from the [GitLab team][team]. If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. +We also use [GitLab Triage] to automate some triaging policies. This is +currently setup as a [scheduled pipeline] running on the [`gl-triage`] branch. + [described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/ [issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815 +[GitLab Triage]: https://gitlab.com/gitlab-org/gitlab-triage +[scheduled pipeline]: https://gitlab.com/gitlab-org/gitlab-ce/pipeline_schedules/3732/edit +[`gl-triage`]: https://gitlab.com/gitlab-org/gitlab-ce/tree/gl-triage ### Feature proposals diff --git a/Dangerfile b/Dangerfile index 84b72673c50470170e91c3791f467a5e39047b0d..9217610da8bf26103fc699bc60718e19a3dd5e15 100644 --- a/Dangerfile +++ b/Dangerfile @@ -4,3 +4,4 @@ danger.import_dangerfile(path: 'danger/changelog') danger.import_dangerfile(path: 'danger/specs') danger.import_dangerfile(path: 'danger/gemfile') danger.import_dangerfile(path: 'danger/database') +danger.import_dangerfile(path: 'danger/frozen_string') diff --git a/Gemfile b/Gemfile index 0ad5df90c3cfcf101aa5cadcebf746c2c8d70194..74e9625d57b1150988416bf5abd8e701fb854ed5 100644 --- a/Gemfile +++ b/Gemfile @@ -220,6 +220,9 @@ gem 'gemnasium-gitlab-service', '~> 0.2' # Slack integration gem 'slack-notifier', '~> 1.5.1' +# Hangouts Chat integration +gem 'hangouts-chat', '~> 0.0.5' + # Asana integration gem 'asana', '~> 0.6.0' @@ -230,7 +233,7 @@ gem 'ruby-fogbugz', '~> 0.2.1' gem 'kubeclient', '~> 3.1.0' # Sanitize user input -gem 'sanitize', '~> 4.6.5' +gem 'sanitize', '~> 4.6' gem 'babosa', '~> 1.0.2' # Sanitizes SVG input @@ -419,7 +422,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.106.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.109.0', require: 'gitaly' gem 'grpc', '~> 1.11.0' # Locked until https://github.com/google/protobuf/issues/4210 is closed diff --git a/Gemfile.lock b/Gemfile.lock index 1d9c75f154ca26b4c12a4cc652a95cf68f1037b0..5bb7bc539625bc0746a1ff1cd8fc5478bc417808 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -284,7 +284,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (0.106.0) + gitaly-proto (0.109.0) google-protobuf (~> 3.1) grpc (~> 1.10) github-linguist (5.3.3) @@ -387,6 +387,7 @@ GEM temple (>= 0.8.0) thor tilt + hangouts-chat (0.0.5) hashdiff (0.3.4) hashie (3.5.7) hashie-forbidden_attributes (0.1.1) @@ -396,7 +397,7 @@ GEM hipchat (1.5.2) httparty mimemagic - html-pipeline (2.8.3) + html-pipeline (2.8.4) activesupport (>= 2) nokogiri (>= 1.4) html2text (0.2.0) @@ -513,7 +514,7 @@ GEM net-ldap (0.16.0) net-ssh (5.0.1) netrc (0.11.0) - nokogiri (1.8.3) + nokogiri (1.8.4) mini_portile2 (~> 2.3.0) nokogumbo (1.5.0) nokogiri @@ -807,7 +808,7 @@ GEM et-orbi (~> 1.0) rugged (0.27.2) safe_yaml (1.0.4) - sanitize (4.6.5) + sanitize (4.6.6) crass (~> 1.0.2) nokogiri (>= 1.4.4) nokogumbo (~> 1.4) @@ -1041,7 +1042,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 0.106.0) + gitaly-proto (~> 0.109.0) github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) @@ -1062,6 +1063,7 @@ DEPENDENCIES grpc (~> 1.11.0) haml_lint (~> 0.26.0) hamlit (~> 2.8.8) + hangouts-chat (~> 0.0.5) hashie-forbidden_attributes health_check (~> 2.6.0) hipchat (~> 1.5.0) @@ -1155,7 +1157,7 @@ DEPENDENCIES ruby_parser (~> 3.8) rufus-scheduler (~> 3.4) rugged (~> 0.27) - sanitize (~> 4.6.5) + sanitize (~> 4.6) sass-rails (~> 5.0.6) scss_lint (~> 0.56.0) seed-fu (~> 2.3.7) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 2d12faa28d907ea851b899107a3ae8a2d721d12d..8c14316a67a1d15476f00e444b7b1a23166a7453 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -287,7 +287,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (0.106.0) + gitaly-proto (0.109.0) google-protobuf (~> 3.1) grpc (~> 1.10) github-linguist (5.3.3) @@ -390,6 +390,7 @@ GEM temple (>= 0.8.0) thor tilt + hangouts-chat (0.0.5) hashdiff (0.3.4) hashie (3.5.7) hashie-forbidden_attributes (0.1.1) @@ -1051,7 +1052,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 0.106.0) + gitaly-proto (~> 0.109.0) github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) @@ -1072,6 +1073,7 @@ DEPENDENCIES grpc (~> 1.11.0) haml_lint (~> 0.26.0) hamlit (~> 2.8.8) + hangouts-chat (~> 0.0.5) hashie-forbidden_attributes health_check (~> 2.6.0) hipchat (~> 1.5.0) @@ -1166,7 +1168,7 @@ DEPENDENCIES ruby_parser (~> 3.8) rufus-scheduler (~> 3.4) rugged (~> 0.27) - sanitize (~> 4.6.5) + sanitize (~> 4.6) sass-rails (~> 5.0.6) scss_lint (~> 0.56.0) seed-fu (~> 2.3.7) diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue index 0fe0007057b38739d23fa05ec1fb400e1e38e7b8..d184a76f0386fb3e07d680245fb291beabcebecd 100644 --- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue +++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue @@ -71,6 +71,11 @@ export default { required: false, default: false, }, + discussions: { + type: Array, + required: false, + default: () => [], + }, }, computed: { ...mapState({ @@ -78,7 +83,6 @@ export default { diffFiles: state => state.diffs.diffFiles, }), ...mapGetters(['isLoggedIn']), - ...mapGetters('diffs', ['discussionsByLineCode']), lineHref() { return this.lineCode ? `#${this.lineCode}` : '#'; }, @@ -88,24 +92,19 @@ export default { this.showCommentButton && !this.isMatchLine && !this.isContextLine && - !this.hasDiscussions && - !this.isMetaLine + !this.isMetaLine && + !this.hasDiscussions ); }, - discussions() { - return this.discussionsByLineCode[this.lineCode] || []; - }, hasDiscussions() { return this.discussions.length > 0; }, shouldShowAvatarsOnGutter() { - let render = this.hasDiscussions && this.showCommentButton; - if (!this.lineType && this.linePosition === LINE_POSITION_RIGHT) { - render = false; + return false; } - return render; + return this.hasDiscussions && this.showCommentButton; }, }, methods: { diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue index 5962f30d9bb1922c72aa2ce6d6ff38ff86154907..e8e8ddc6c5e7d9fc6809b35a86600bef17b68f34 100644 --- a/app/assets/javascripts/diffs/components/diff_table_cell.vue +++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue @@ -67,6 +67,11 @@ export default { required: false, default: false, }, + discussions: { + type: Array, + required: false, + default: () => [], + }, }, computed: { ...mapGetters(['isLoggedIn']), @@ -136,6 +141,7 @@ export default { :is-match-line="isMatchLine" :is-context-line="isContentLine" :is-meta-line="isMetaLine" + :discussions="discussions" /> </td> </template> diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue index a6f011ff31e4dd3ec41ef74bec7203bca91bf081..1b5ae5e9f75b0583a80ff17cfe4e62c1eb1dd0a3 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue @@ -1,5 +1,5 @@ <script> -import { mapState, mapGetters } from 'vuex'; +import { mapState } from 'vuex'; import diffDiscussions from './diff_discussions.vue'; import diffLineNoteForm from './diff_line_note_form.vue'; @@ -21,15 +21,16 @@ export default { type: Number, required: true, }, + discussions: { + type: Array, + required: false, + default: () => [], + }, }, computed: { ...mapState({ diffLineCommentForms: state => state.diffs.diffLineCommentForms, }), - ...mapGetters('diffs', ['discussionsByLineCode']), - discussions() { - return this.discussionsByLineCode[this.line.lineCode] || []; - }, className() { return this.discussions.length ? '' : 'js-temp-notes-holder'; }, diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue index 0e306f39a9f9c32326b53c25277cc3a8c2bd88aa..32d65ff994ff522f411ac8ed89d420d4bfcfd554 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue @@ -33,6 +33,11 @@ export default { required: false, default: false, }, + discussions: { + type: Array, + required: false, + default: () => [], + }, }, data() { return { @@ -89,6 +94,7 @@ export default { :is-bottom="isBottom" :is-hover="isHover" :show-comment-button="true" + :discussions="discussions" class="diff-line-num old_line" /> <diff-table-cell @@ -98,6 +104,7 @@ export default { :line-type="newLineType" :is-bottom="isBottom" :is-hover="isHover" + :discussions="discussions" class="diff-line-num new_line" /> <td diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue index 8e491d293e5f83efba04eb92135e4fa332aefd92..5f30cc57a5962d8cc3fbaa103ad2c511358e386d 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_view.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue @@ -20,7 +20,11 @@ export default { }, }, computed: { - ...mapGetters('diffs', ['commitId', 'discussionsByLineCode']), + ...mapGetters('diffs', [ + 'commitId', + 'shouldRenderInlineCommentRow', + 'singleDiscussionByLineCode', + ]), ...mapState({ diffLineCommentForms: state => state.diffs.diffLineCommentForms, }), @@ -34,18 +38,7 @@ export default { return window.gon.user_color_scheme; }, }, - methods: { - shouldRenderCommentRow(line) { - if (this.diffLineCommentForms[line.lineCode]) return true; - - const lineDiscussions = this.discussionsByLineCode[line.lineCode]; - if (lineDiscussions === undefined) { - return false; - } - - return lineDiscussions.every(discussion => discussion.expanded); - }, - }, + methods: {}, }; </script> @@ -64,13 +57,15 @@ export default { :line="line" :is-bottom="index + 1 === diffLinesLength" :key="line.lineCode" + :discussions="singleDiscussionByLineCode(line.lineCode)" /> <inline-diff-comment-row - v-if="shouldRenderCommentRow(line)" + v-if="shouldRenderInlineCommentRow(line)" :diff-file-hash="diffFile.fileHash" :line="line" :line-index="index" :key="index" + :discussions="singleDiscussionByLineCode(line.lineCode)" /> </template> </tbody> diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue index 05e5cafc717188d9a8db12399624a0bc989bec30..bb9a65c83fad2d832abe20abf4b2998782ef0945 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue @@ -1,5 +1,5 @@ <script> -import { mapState, mapGetters } from 'vuex'; +import { mapState } from 'vuex'; import diffDiscussions from './diff_discussions.vue'; import diffLineNoteForm from './diff_line_note_form.vue'; @@ -21,48 +21,51 @@ export default { type: Number, required: true, }, + leftDiscussions: { + type: Array, + required: false, + default: () => [], + }, + rightDiscussions: { + type: Array, + required: false, + default: () => [], + }, }, computed: { ...mapState({ diffLineCommentForms: state => state.diffs.diffLineCommentForms, }), - ...mapGetters('diffs', ['discussionsByLineCode']), leftLineCode() { return this.line.left.lineCode; }, rightLineCode() { return this.line.right.lineCode; }, - hasDiscussion() { - const discussions = this.discussionsByLineCode; - - return discussions[this.leftLineCode] || discussions[this.rightLineCode]; - }, hasExpandedDiscussionOnLeft() { - const discussions = this.discussionsByLineCode[this.leftLineCode]; - + const discussions = this.leftDiscussions; return discussions ? discussions.every(discussion => discussion.expanded) : false; }, hasExpandedDiscussionOnRight() { - const discussions = this.discussionsByLineCode[this.rightLineCode]; - + const discussions = this.rightDiscussions; return discussions ? discussions.every(discussion => discussion.expanded) : false; }, hasAnyExpandedDiscussion() { return this.hasExpandedDiscussionOnLeft || this.hasExpandedDiscussionOnRight; }, shouldRenderDiscussionsOnLeft() { - return this.discussionsByLineCode[this.leftLineCode] && this.hasExpandedDiscussionOnLeft; + return this.leftDiscussions && this.hasExpandedDiscussionOnLeft; }, shouldRenderDiscussionsOnRight() { - return ( - this.discussionsByLineCode[this.rightLineCode] && - this.hasExpandedDiscussionOnRight && - this.line.right.type - ); + return this.rightDiscussions && this.hasExpandedDiscussionOnRight && this.line.right.type; + }, + showRightSideCommentForm() { + return this.line.right.type && this.diffLineCommentForms[this.rightLineCode]; }, className() { - return this.hasDiscussion ? '' : 'js-temp-notes-holder'; + return this.leftDiscussions.length > 0 || this.rightDiscussions.length > 0 + ? '' + : 'js-temp-notes-holder'; }, }, }; @@ -80,13 +83,12 @@ export default { class="content" > <diff-discussions - v-if="discussionsByLineCode[leftLineCode].length" - :discussions="discussionsByLineCode[leftLineCode]" + v-if="leftDiscussions.length" + :discussions="leftDiscussions" /> </div> <diff-line-note-form - v-if="diffLineCommentForms[leftLineCode] && - diffLineCommentForms[leftLineCode]" + v-if="diffLineCommentForms[leftLineCode]" :diff-file-hash="diffFileHash" :line="line.left" :note-target-line="line.left" @@ -100,13 +102,12 @@ export default { class="content" > <diff-discussions - v-if="discussionsByLineCode[rightLineCode].length" - :discussions="discussionsByLineCode[rightLineCode]" + v-if="rightDiscussions.length" + :discussions="rightDiscussions" /> </div> <diff-line-note-form - v-if="diffLineCommentForms[rightLineCode] && - diffLineCommentForms[rightLineCode] && line.right.type" + v-if="showRightSideCommentForm" :diff-file-hash="diffFileHash" :line="line.right" :note-target-line="line.right" diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue index 0031cedc68f9a4731c84dbb3373e17201ca82b55..d4e54c2bd00c266736acf478117a7d2920f78218 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue @@ -36,6 +36,16 @@ export default { required: false, default: false, }, + leftDiscussions: { + type: Array, + required: false, + default: () => [], + }, + rightDiscussions: { + type: Array, + required: false, + default: () => [], + }, }, data() { return { @@ -116,6 +126,7 @@ export default { :is-hover="isLeftHover" :show-comment-button="true" :diff-view-type="parallelDiffViewType" + :discussions="leftDiscussions" class="diff-line-num old_line" /> <td @@ -136,6 +147,7 @@ export default { :is-hover="isRightHover" :show-comment-button="true" :diff-view-type="parallelDiffViewType" + :discussions="rightDiscussions" class="diff-line-num new_line" /> <td diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue index 8f8d6bbc81825ae7a0867423aa472b3812d6d259..4d97cb6d15dd0a20ec0e07a79bf48fc8d61c89c5 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue @@ -21,7 +21,11 @@ export default { }, }, computed: { - ...mapGetters('diffs', ['commitId', 'discussionsByLineCode']), + ...mapGetters('diffs', [ + 'commitId', + 'singleDiscussionByLineCode', + 'shouldRenderParallelCommentRow', + ]), ...mapState({ diffLineCommentForms: state => state.diffs.diffLineCommentForms, }), @@ -51,32 +55,6 @@ export default { return window.gon.user_color_scheme; }, }, - methods: { - shouldRenderCommentRow(line) { - const leftLineCode = line.left.lineCode; - const rightLineCode = line.right.lineCode; - const discussions = this.discussionsByLineCode; - const leftDiscussions = discussions[leftLineCode]; - const rightDiscussions = discussions[rightLineCode]; - const hasDiscussion = leftDiscussions || rightDiscussions; - - const hasExpandedDiscussionOnLeft = leftDiscussions - ? leftDiscussions.every(discussion => discussion.expanded) - : false; - const hasExpandedDiscussionOnRight = rightDiscussions - ? rightDiscussions.every(discussion => discussion.expanded) - : false; - - if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) { - return true; - } - - const hasCommentFormOnLeft = this.diffLineCommentForms[leftLineCode]; - const hasCommentFormOnRight = this.diffLineCommentForms[rightLineCode]; - - return hasCommentFormOnLeft || hasCommentFormOnRight; - }, - }, }; </script> @@ -97,13 +75,17 @@ export default { :line="line" :is-bottom="index + 1 === diffLinesLength" :key="index" + :left-discussions="singleDiscussionByLineCode(line.left.lineCode)" + :right-discussions="singleDiscussionByLineCode(line.right.lineCode)" /> <parallel-diff-comment-row - v-if="shouldRenderCommentRow(line)" + v-if="shouldRenderParallelCommentRow(line)" :key="`dcr-${index}`" :line="line" :diff-file-hash="diffFile.fileHash" :line-index="index" + :left-discussions="singleDiscussionByLineCode(line.left.lineCode)" + :right-discussions="singleDiscussionByLineCode(line.right.lineCode)" /> </template> </tbody> diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js index d3881fa1a0a55c0db256b23329b62f2af69dcbbb..c7b9b1a16e6ca43be7ce2186fddb4cbf02fab88d 100644 --- a/app/assets/javascripts/diffs/store/getters.js +++ b/app/assets/javascripts/diffs/store/getters.js @@ -75,19 +75,21 @@ export const discussionsByLineCode = (state, getters, rootState, rootGetters) => const isDiffDiscussion = note.diff_discussion; const hasLineCode = note.line_code; const isResolvable = note.resolvable; - const diffRefs = diffRefsByLineCode[note.line_code]; - if (isDiffDiscussion && hasLineCode && isResolvable && diffRefs) { - const refs = convertObjectPropsToCamelCase(note.position.formatter); - const originalRefs = convertObjectPropsToCamelCase(note.original_position.formatter); + if (isDiffDiscussion && hasLineCode && isResolvable) { + const diffRefs = diffRefsByLineCode[note.line_code]; + if (diffRefs) { + const refs = convertObjectPropsToCamelCase(note.position.formatter); + const originalRefs = convertObjectPropsToCamelCase(note.original_position.formatter); - if (_.isEqual(refs, diffRefs) || _.isEqual(originalRefs, diffRefs)) { - const lineCode = note.line_code; + if (_.isEqual(refs, diffRefs) || _.isEqual(originalRefs, diffRefs)) { + const lineCode = note.line_code; - if (acc[lineCode]) { - acc[lineCode].push(note); - } else { - acc[lineCode] = [note]; + if (acc[lineCode]) { + acc[lineCode].push(note); + } else { + acc[lineCode] = [note]; + } } } } @@ -96,6 +98,47 @@ export const discussionsByLineCode = (state, getters, rootState, rootGetters) => }, {}); }; +export const singleDiscussionByLineCode = (state, getters) => lineCode => { + if (!lineCode) return []; + const discussions = getters.discussionsByLineCode; + return discussions[lineCode] || []; +}; + +export const shouldRenderParallelCommentRow = (state, getters) => line => { + const leftLineCode = line.left.lineCode; + const rightLineCode = line.right.lineCode; + const leftDiscussions = getters.singleDiscussionByLineCode(leftLineCode); + const rightDiscussions = getters.singleDiscussionByLineCode(rightLineCode); + const hasDiscussion = leftDiscussions.length || rightDiscussions.length; + + const hasExpandedDiscussionOnLeft = leftDiscussions.length + ? leftDiscussions.every(discussion => discussion.expanded) + : false; + const hasExpandedDiscussionOnRight = rightDiscussions.length + ? rightDiscussions.every(discussion => discussion.expanded) + : false; + + if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) { + return true; + } + + const hasCommentFormOnLeft = state.diffLineCommentForms[leftLineCode]; + const hasCommentFormOnRight = state.diffLineCommentForms[rightLineCode]; + + return hasCommentFormOnLeft || hasCommentFormOnRight; +}; + +export const shouldRenderInlineCommentRow = (state, getters) => line => { + if (state.diffLineCommentForms[line.lineCode]) return true; + + const lineDiscussions = getters.singleDiscussionByLineCode(line.lineCode); + if (lineDiscussions.length === 0) { + return false; + } + + return lineDiscussions.every(discussion => discussion.expanded); +}; + // prevent babel-plugin-rewire from generating an invalid default during karma∂ tests export const getDiffFileByHash = state => fileHash => state.diffFiles.find(file => file.fileHash === fileHash); diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js index 029fd6a67d444e52b63660b3657ec7f321dddd30..efba6fc1affa935fc67a0ce16bfe353258398f65 100644 --- a/app/assets/javascripts/gpg_badges.js +++ b/app/assets/javascripts/gpg_badges.js @@ -1,23 +1,36 @@ import $ from 'jquery'; import { parseQueryStringIntoObject } from '~/lib/utils/common_utils'; import axios from '~/lib/utils/axios_utils'; -import flash from '~/flash'; +import createFlash from '~/flash'; import { __ } from '~/locale'; export default class GpgBadges { static fetch() { - const badges = $('.js-loading-gpg-badge'); const tag = $('.js-signature-container'); + if (tag.length === 0) { + return Promise.resolve(); + } + + const badges = $('.js-loading-gpg-badge'); badges.html('<i class="fa fa-spinner fa-spin"></i>'); + const displayError = () => createFlash(__('An error occurred while loading commit signatures')); + + const endpoint = tag.data('signaturesPath'); + if (!endpoint) { + displayError(); + return Promise.reject(new Error('Missing commit signatures endpoint!')); + } + const params = parseQueryStringIntoObject(tag.serialize()); - return axios.get(tag.data('signaturesPath'), { params }) - .then(({ data }) => { - data.signatures.forEach((signature) => { - badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html); - }); - }) - .catch(() => flash(__('An error occurred while loading commits'))); + return axios + .get(endpoint, { params }) + .then(({ data }) => { + data.signatures.forEach(signature => { + badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html); + }); + }) + .catch(displayError); } } diff --git a/app/assets/javascripts/issue_show/components/edited.vue b/app/assets/javascripts/issue_show/components/edited.vue index 5ff5b1630b1e2fae40906698b34893be2497012e..05cd976f1968bbfd66b5bba2f548bdb857154c09 100644 --- a/app/assets/javascripts/issue_show/components/edited.vue +++ b/app/assets/javascripts/issue_show/components/edited.vue @@ -46,7 +46,7 @@ by <a :href="updatedByPath" - class="author_link" + class="author-link" > <span>{{ updatedByName }}</span> </a> diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 6b7550efff8b56921825aa1e1566f499b1a942fe..2f3dd6f6cbc7286a6fa602b89b9d282bfbd75170 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -541,6 +541,26 @@ export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => { }); }; +/** + * Method to round of values with decimal places + * with provided precision. + * + * Taken from https://stackoverflow.com/a/7343013/414749 + * + * Eg; roundOffFloat(3.141592, 3) = 3.142 + * + * Refer to spec/javascripts/lib/utils/common_utils_spec.js for + * more supported examples. + * + * @param {Float} number + * @param {Number} precision + */ +export const roundOffFloat = (number, precision = 0) => { + // eslint-disable-next-line no-restricted-properties + const multiplier = Math.pow(10, precision); + return Math.round(number * multiplier) / multiplier; +}; + window.gl = window.gl || {}; window.gl.utils = { ...(window.gl.utils || {}), diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js index 7fca80c2fdb3207463402681416c2b6869b21b5b..91d8c30744fc1153d52b531ec70368ba0479fd05 100644 --- a/app/assets/javascripts/lib/utils/poll.js +++ b/app/assets/javascripts/lib/utils/poll.js @@ -38,7 +38,7 @@ import { normalizeHeaders } from './common_utils'; * } else { * poll.stop(); * } -* }); + * }); * * 1. Checks for response and headers before start polling * 2. Interval is provided by `Poll-Interval` header. @@ -51,8 +51,8 @@ export default class Poll { constructor(options = {}) { this.options = options; this.options.data = options.data || {}; - this.options.notificationCallback = options.notificationCallback || - function notificationCallback() {}; + this.options.notificationCallback = + options.notificationCallback || function notificationCallback() {}; this.intervalHeader = 'POLL-INTERVAL'; this.timeoutID = null; @@ -63,6 +63,7 @@ export default class Poll { const headers = normalizeHeaders(response.headers); const pollInterval = parseInt(headers[this.intervalHeader], 10); if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) { + clearTimeout(this.timeoutID); this.timeoutID = setTimeout(() => { this.makeRequest(); }, pollInterval); @@ -77,11 +78,11 @@ export default class Poll { notificationCallback(true); return resource[method](data) - .then((response) => { + .then(response => { this.checkConditions(response); notificationCallback(false); }) - .catch((error) => { + .catch(error => { notificationCallback(false); if (error.status === httpStatusCodes.ABORTED) { return; diff --git a/app/assets/javascripts/notes/components/note_edited_text.vue b/app/assets/javascripts/notes/components/note_edited_text.vue index 391bb2ae179549fffede739c0fe59bd879d743e0..d848335022f00685563a135c1273e0918126bc8d 100644 --- a/app/assets/javascripts/notes/components/note_edited_text.vue +++ b/app/assets/javascripts/notes/components/note_edited_text.vue @@ -42,7 +42,7 @@ export default { by <a :href="editedBy.path" - class="js-vue-author author_link"> + class="js-vue-author author-link"> {{ editedBy.name }} </a> </template> diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index 6dd4c9d66acb975f1b62bfa2d6dda6f9339d4f42..3aef30c608c002763274977c3ebb89c48afb2c6c 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -15,7 +15,7 @@ document.addEventListener('DOMContentLoaded', () => { const notesDataset = document.getElementById('js-vue-notes').dataset; const parsedUserData = JSON.parse(notesDataset.currentUserData); const noteableData = JSON.parse(notesDataset.noteableData); - const { markdownVersion } = notesDataset; + const markdownVersion = parseInt(notesDataset.markdownVersion, 10); let currentUserData = {}; noteableData.noteableType = notesDataset.noteableType; diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js index ab6a95e2601bb945dca326598b6f421f9e938310..e1b159142c940d8ff9e04b2d5b9777c48d4a44d0 100644 --- a/app/assets/javascripts/notes/stores/mutations.js +++ b/app/assets/javascripts/notes/stores/mutations.js @@ -174,27 +174,19 @@ export default { [types.UPDATE_NOTE](state, note) { const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id); - if (noteObj.individual_note) { noteObj.notes.splice(0, 1, note); } else { const comment = utils.findNoteObjectById(noteObj.notes, note.id); - noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note); + Object.assign(comment, note); } }, [types.UPDATE_DISCUSSION](state, noteData) { const note = noteData; - let index = 0; - - state.discussions.forEach((n, i) => { - if (n.id === note.id) { - index = i; - } - }); - + const selectedDiscussion = state.discussions.find(n => n.id === note.id); note.expanded = true; // override expand flag to prevent collapse - state.discussions.splice(index, 1, note); + Object.assign(selectedDiscussion, note); }, [types.CLOSE_ISSUE](state) { @@ -215,12 +207,9 @@ export default { [types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) { const discussion = utils.findNoteObjectById(state.discussions, discussionId); - const index = state.discussions.indexOf(discussion); - const discussionWithDiffLines = Object.assign({}, discussion, { + Object.assign(discussion, { truncated_diff_lines: diffLines, }); - - state.discussions.splice(index, 1, discussionWithDiffLines); }, }; diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js index a0e096ebfafa8e5077d9802236204a36cde69be6..c4a812c5af437d658bd2bc97a4238f5455f4f25f 100644 --- a/app/assets/javascripts/notes/stores/utils.js +++ b/app/assets/javascripts/notes/stores/utils.js @@ -2,13 +2,11 @@ import AjaxCache from '~/lib/utils/ajax_cache'; const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm; -export const findNoteObjectById = (notes, id) => - notes.filter(n => n.id === id)[0]; +export const findNoteObjectById = (notes, id) => notes.find(n => n.id === id); export const getQuickActionText = note => { let text = 'Applying command'; - const quickActions = - AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || []; + const quickActions = AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || []; const executedCommands = quickActions.filter(command => { const commandRegex = new RegExp(`/${command.name}`); @@ -29,5 +27,4 @@ export const getQuickActionText = note => { export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note); -export const stripQuickActions = note => - note.replace(REGEX_QUICK_ACTIONS, '').trim(); +export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim(); diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js index 85c6862d629bcbdf053be8bc0898397ea5624412..84e5bb3c46e271c8639014cb4b3f27af72e815fd 100644 --- a/app/assets/javascripts/pages/projects/blob/show/index.js +++ b/app/assets/javascripts/pages/projects/blob/show/index.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import BlobViewer from '~/blob/viewer/index'; import initBlob from '~/pages/projects/init_blob'; +import GpgBadges from '~/gpg_badges'; document.addEventListener('DOMContentLoaded', () => { new BlobViewer(); // eslint-disable-line no-new @@ -26,4 +27,6 @@ document.addEventListener('DOMContentLoaded', () => { }, }); } + + GpgBadges.fetch(); }); diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 3b0f0f960b82f88c8b00c4d265613ca6220024f5..d2dc0c4570e91bf840ae3f3300cd384bb2a9c9f5 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -7,6 +7,7 @@ import TreeView from '~/tree'; import BlobViewer from '~/blob/viewer/index'; import Activities from '~/activities'; import { ajaxGet } from '~/lib/utils/common_utils'; +import GpgBadges from '~/gpg_badges'; import Star from '../../../star'; import notificationsDropdown from '../../../notifications_dropdown'; @@ -38,4 +39,6 @@ document.addEventListener('DOMContentLoaded', () => { $(treeSlider).waitForImages(() => { ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); }); + + GpgBadges.fetch(); }); diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index 7ad082a5e6167cc050477c02eb27005af2845979..33d69d891d847f6f85b22845124ad8b84ad4ac6f 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import Vue from 'vue'; import initBlob from '~/blob_edit/blob_bundle'; import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; +import GpgBadges from '~/gpg_badges'; import TreeView from '../../../../tree'; import ShortcutsNavigation from '../../../../shortcuts_navigation'; import BlobViewer from '../../../../blob/viewer'; @@ -14,7 +15,8 @@ document.addEventListener('DOMContentLoaded', () => { new BlobViewer(); // eslint-disable-line no-new new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new $('#tree-slider').waitForImages(() => - ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); + ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath), + ); initBlob(); const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status'); @@ -36,4 +38,6 @@ document.addEventListener('DOMContentLoaded', () => { }, }); } + + GpgBadges.fetch(); }); diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue index a4c7c143e5618272e67f19ce2d66eda474048127..1c1e17563a13e59a74983a5c094da3f387a5dbcd 100644 --- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue +++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue @@ -1,27 +1,27 @@ <script> - import Visibility from 'visibilityjs'; - import ciIcon from '~/vue_shared/components/ci_icon.vue'; - import loadingIcon from '~/vue_shared/components/loading_icon.vue'; - import Poll from '~/lib/utils/poll'; - import Flash from '~/flash'; - import { s__, sprintf } from '~/locale'; - import tooltip from '~/vue_shared/directives/tooltip'; - import CommitPipelineService from '../services/commit_pipeline_service'; +import Visibility from 'visibilityjs'; +import ciIcon from '~/vue_shared/components/ci_icon.vue'; +import loadingIcon from '~/vue_shared/components/loading_icon.vue'; +import Poll from '~/lib/utils/poll'; +import Flash from '~/flash'; +import { s__, sprintf } from '~/locale'; +import tooltip from '~/vue_shared/directives/tooltip'; +import CommitPipelineService from '../services/commit_pipeline_service'; - export default { - directives: { - tooltip, +export default { + directives: { + tooltip, + }, + components: { + ciIcon, + loadingIcon, + }, + props: { + endpoint: { + type: String, + required: true, }, - components: { - ciIcon, - loadingIcon, - }, - props: { - endpoint: { - type: String, - required: true, - }, - /* This prop can be used to replace some of the `render_commit_status` + /* This prop can be used to replace some of the `render_commit_status` used across GitLab, this way we could use this vue component and add a realtime status where it makes sense realtime: { @@ -29,76 +29,77 @@ required: false, default: true, }, */ + }, + data() { + return { + ciStatus: {}, + isLoading: true, + }; + }, + computed: { + statusTitle() { + return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text }); }, - data() { - return { - ciStatus: {}, - isLoading: true, - }; - }, - computed: { - statusTitle() { - return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text }); - }, + }, + mounted() { + this.service = new CommitPipelineService(this.endpoint); + this.initPolling(); + }, + methods: { + successCallback(res) { + const { pipelines } = res.data; + if (pipelines.length > 0) { + // The pipeline entity always keeps the latest pipeline info on the `details.status` + this.ciStatus = pipelines[0].details.status; + } + this.isLoading = false; }, - mounted() { - this.service = new CommitPipelineService(this.endpoint); - this.initPolling(); + errorCallback() { + this.ciStatus = { + text: 'not found', + icon: 'status_notfound', + group: 'notfound', + }; + this.isLoading = false; + Flash(s__('Something went wrong on our end')); }, - methods: { - successCallback(res) { - const { pipelines } = res.data; - if (pipelines.length > 0) { - // The pipeline entity always keeps the latest pipeline info on the `details.status` - this.ciStatus = pipelines[0].details.status; - } - this.isLoading = false; - }, - errorCallback() { - this.ciStatus = { - text: 'not found', - icon: 'status_notfound', - group: 'notfound', - }; - this.isLoading = false; - Flash(s__('Something went wrong on our end')); - }, - initPolling() { - this.poll = new Poll({ - resource: this.service, - method: 'fetchData', - successCallback: response => this.successCallback(response), - errorCallback: this.errorCallback, - }); + initPolling() { + this.poll = new Poll({ + resource: this.service, + method: 'fetchData', + successCallback: response => this.successCallback(response), + errorCallback: this.errorCallback, + }); + + if (!Visibility.hidden()) { + this.isLoading = true; + this.poll.makeRequest(); + } else { + this.fetchPipelineCommitData(); + } + Visibility.change(() => { if (!Visibility.hidden()) { - this.isLoading = true; - this.poll.makeRequest(); + this.poll.restart(); } else { - this.fetchPipelineCommitData(); + this.poll.stop(); } - - Visibility.change(() => { - if (!Visibility.hidden()) { - this.poll.restart(); - } else { - this.poll.stop(); - } - }); - }, - fetchPipelineCommitData() { - this.service.fetchData() - .then(this.successCallback) - .catch(this.errorCallback); - }, + }); }, - destroy() { - this.poll.stop(); + fetchPipelineCommitData() { + this.service + .fetchData() + .then(this.successCallback) + .catch(this.errorCallback); }, - }; + }, + destroy() { + this.poll.stop(); + }, +}; </script> <template> - <div> + <div class="ci-status-link"> <loading-icon v-if="isLoading" label="Loading pipeline status" @@ -113,6 +114,7 @@ :title="statusTitle" :aria-label="statusTitle" :status="ciStatus" + :size="24" data-container="body" /> </a> diff --git a/app/assets/javascripts/reports/store/actions.js b/app/assets/javascripts/reports/store/actions.js new file mode 100644 index 0000000000000000000000000000000000000000..15c077b0fd8762316bfa0307a0140e6ba4197f60 --- /dev/null +++ b/app/assets/javascripts/reports/store/actions.js @@ -0,0 +1,67 @@ +import Visibility from 'visibilityjs'; +import axios from '../../lib/utils/axios_utils'; +import Poll from '../../lib/utils/poll'; +import * as types from './mutation_types'; + +export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint); + +export const requestReports = ({ commit }) => commit(types.REQUEST_REPORTS); + +let eTagPoll; + +export const clearEtagPoll = () => { + eTagPoll = null; +}; + +export const stopPolling = () => { + if (eTagPoll) eTagPoll.stop(); +}; + +export const restartPolling = () => { + if (eTagPoll) eTagPoll.restart(); +}; + +/** + * We need to poll the reports endpoint while they are being parsed in the Backend. + * This can take up to one minute. + * + * Poll.js will handle etag response. + * While http status code is 204, it means it's parsing, and we'll keep polling + * When http status code is 200, it means parsing is done, we can show the results & stop polling + * When http status code is 500, it means parsing went wrong and we stop polling + */ +export const fetchReports = ({ state, dispatch }) => { + dispatch('requestReports'); + + eTagPoll = new Poll({ + resource: { + getReports(endpoint) { + return axios.get(endpoint); + }, + }, + data: state.endpoint, + method: 'getReports', + successCallback: ({ data }) => dispatch('receiveReportsSuccess', data), + errorCallback: () => dispatch('receiveReportsError'), + }); + + if (!Visibility.hidden()) { + eTagPoll.makeRequest(); + } + + Visibility.change(() => { + if (!Visibility.hidden()) { + dispatch('restartPolling'); + } else { + dispatch('stopPolling'); + } + }); +}; + +export const receiveReportsSuccess = ({ commit }, response) => + commit(types.RECEIVE_REPORTS_SUCCESS, response); + +export const receiveReportsError = ({ commit }) => commit(types.RECEIVE_REPORTS_ERROR); + +// prevent babel-plugin-rewire from generating an invalid default during karma tests +export default () => {}; diff --git a/app/assets/javascripts/reports/store/index.js b/app/assets/javascripts/reports/store/index.js new file mode 100644 index 0000000000000000000000000000000000000000..af4f9688fb47a9601b843a3b3f25a80aeb26c978 --- /dev/null +++ b/app/assets/javascripts/reports/store/index.js @@ -0,0 +1,13 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import * as actions from './actions'; +import mutations from './mutations'; +import state from './state'; + +Vue.use(Vuex); + +export default () => new Vuex.Store({ + actions, + mutations, + state: state(), +}); diff --git a/app/assets/javascripts/reports/store/mutation_types.js b/app/assets/javascripts/reports/store/mutation_types.js new file mode 100644 index 0000000000000000000000000000000000000000..77722974c45750b06413a34291504edb25e50a67 --- /dev/null +++ b/app/assets/javascripts/reports/store/mutation_types.js @@ -0,0 +1,5 @@ +export const SET_ENDPOINT = 'SET_ENDPOINT'; + +export const REQUEST_REPORTS = 'REQUEST_REPORTS'; +export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS'; +export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR'; diff --git a/app/assets/javascripts/reports/store/mutations.js b/app/assets/javascripts/reports/store/mutations.js new file mode 100644 index 0000000000000000000000000000000000000000..d9d301826cf02f775155ecf2be2375e45f253395 --- /dev/null +++ b/app/assets/javascripts/reports/store/mutations.js @@ -0,0 +1,26 @@ +/* eslint-disable no-param-reassign */ +import * as types from './mutation_types'; + +export default { + [types.SET_ENDPOINT](state, endpoint) { + state.endpoint = endpoint; + }, + [types.REQUEST_REPORTS](state) { + state.isLoading = true; + }, + [types.RECEIVE_REPORTS_SUCCESS](state, response) { + + state.isLoading = false; + + state.summary.total = response.summary.total; + state.summary.resolved = response.summary.resolved; + state.summary.failed = response.summary.failed; + + state.reports = response.suites; + + }, + [types.RECEIVE_REPORTS_ERROR](state) { + state.isLoading = false; + state.hasError = true; + }, +}; diff --git a/app/assets/javascripts/reports/store/state.js b/app/assets/javascripts/reports/store/state.js new file mode 100644 index 0000000000000000000000000000000000000000..97f9d0a6859ccd8a02bef6c9ca91fa78428d7532 --- /dev/null +++ b/app/assets/javascripts/reports/store/state.js @@ -0,0 +1,28 @@ +export default () => ({ + endpoint: null, + + isLoading: false, + hasError: false, + + summary: { + total: 0, + resolved: 0, + failed: 0, + }, + + /** + * Each report will have the following format: + * { + * name: {String}, + * summary: { + * total: {Number}, + * resolved: {Number}, + * failed: {Number}, + * }, + * new_failures: {Array.<Object>}, + * resolved_failures: {Array.<Object>}, + * existing_failures: {Array.<Object>}, + * } + */ + reports: [], +}); diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue index d22a1e1ac66d9b9f22e350adbc06fa9e60855daa..dd155c133cea209dada67db251c86e88200889a8 100644 --- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue +++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue @@ -187,7 +187,7 @@ export default { <template v-else-if="hasOneUser"> <a :href="assigneeUrl(firstUser)" - class="author_link bold" + class="author-link bold" > <img :alt="assigneeAlt(firstUser)" diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue index 33dd6c981b6f0dde423c364e5c71d98dceba9c1b..56d57f6aac89d2e52df60fa40140ce121b8e74a2 100644 --- a/app/assets/javascripts/sidebar/components/participants/participants.vue +++ b/app/assets/javascripts/sidebar/components/participants/participants.vue @@ -120,7 +120,7 @@ > <a :href="participant.web_url" - class="author_link" + class="author-link" > <user-avatar-image :lazy="true" diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index e3d7645040dc945c811c091fcca8e6b8b02e17a2..e19bbbacf4d7b03c8f1d1b8c08dd4dd53ead49eb 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -206,8 +206,8 @@ function UsersSelect(currentUser, els, options = {}) { return $collapsedSidebar.html(collapsedAssigneeTemplate(user)); }); }; - collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <a class="author_link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>'); - assigneeTemplate = _.template('<% if (username) { %> <a class="author_link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>'); + collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <a class="author-link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>'); + assigneeTemplate = _.template('<% if (username) { %> <a class="author-link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>'); return $dropdown.glDropdown({ showMenuAbove: showMenuAbove, data: function(term, callback) { diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue index dc5760bce287de95034e53c76bc28fc795abb9f3..d272bf3f55faca2ab85720ac474530ffcb90e12d 100644 --- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue +++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue @@ -13,12 +13,19 @@ * /> */ import tooltip from '../directives/tooltip'; +import Icon from '../components/icon.vue'; export default { name: 'ClipboardButton', + directives: { tooltip, }, + + components: { + Icon, + }, + props: { text: { type: String, @@ -58,10 +65,6 @@ export default { type="button" class="btn" > - <i - aria-hidden="true" - class="fa fa-clipboard" - > - </i> + <icon name="duplicate" /> </button> </template> 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 b1c2df54ef661cd378bacc5fcba6fd428b583da9..f44d361c47e29fc3e7c81f450c18be49586a95dc 100644 --- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue +++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue @@ -1,4 +1,5 @@ <script> +import { roundOffFloat } from '~/lib/utils/common_utils'; import tooltip from '~/vue_shared/directives/tooltip'; export default { @@ -70,7 +71,7 @@ export default { }, methods: { getPercent(count) { - return Math.ceil((count / this.totalCount) * 100); + return roundOffFloat((count / this.totalCount) * 100, 1); }, barStyle(percent) { return `width: ${percent}%;`; diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index c1ec11e434a037405f51bee4e5d1f2ba92f7d26a..94fa799313346d46ab34d8835c900dd07acb3f08 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -7,7 +7,7 @@ .avatar-circle { float: left; margin-right: 15px; - border-radius: $avatar_radius; + border-radius: $avatar-radius; border: 1px solid $avatar-border; &.s16 { @include avatar-size(16px, 6px); } &.s18 { @include avatar-size(18px, 6px); } @@ -110,7 +110,7 @@ color: $white-light; border: 1px solid $avatar-border; border-radius: 1em; - font-family: $regular_font; + font-family: $regular-font; font-size: 9px; line-height: 16px; text-align: center; diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 523fcb05a874133597f90c885d5fa6b2680f4c54..646cedd79ed471bca30c6bec634a48adf8760de5 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -294,6 +294,10 @@ .btn-clipboard { border: 0; padding: 0 5px; + + svg { + top: auto; + } } .input-group-prepend, diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 218e37602dd994cf9a17bd5a18c2ef06ac42b995..637587de597ad042138934b81c25e9eeb5fb6784 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -113,8 +113,6 @@ hr { .item-title { font-weight: $gl-font-weight-bold; } -/** FLASH message **/ -.author_link, .author-link { color: $gl-link-color; } diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index a10ff3eecb33819efb9beafcd6240acc8f5efc39..d7149d9362227c3410aba5a7c88064e9e8672d38 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -80,7 +80,7 @@ label { .form-control { height: 29px; background: $white-light; - font-family: $monospace_font; + font-family: $monospace-font; } .input-group-prepend .btn, diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss index 813a1711ea2fb3e60d54dc949f038ff598fd8af2..452e946f95f144a152cf7612d7d20ad87ca07245 100644 --- a/app/assets/stylesheets/framework/highlight.scss +++ b/app/assets/stylesheets/framework/highlight.scss @@ -9,8 +9,8 @@ padding: 10px 0; border: 0; border-radius: 0; - font-family: $monospace_font; - font-size: $code_font_size; + font-family: $monospace-font; + font-size: $code-font-size; line-height: 19px; margin: 0; overflow: auto; @@ -22,7 +22,7 @@ code { display: inline-block; min-width: 100%; - font-family: $monospace_font; + font-family: $monospace-font; white-space: normal; word-wrap: normal; padding: 0; @@ -44,7 +44,7 @@ float: left; a { - font-family: $monospace_font; + font-family: $monospace-font; display: block; font-size: $code_font_size !important; min-height: 19px; diff --git a/app/assets/stylesheets/framework/jquery.scss b/app/assets/stylesheets/framework/jquery.scss index 300ba4f2de62cd5d5a0a390687a61e98757f6571..d1360a0c0eb97175a49e54f7128290a6d05fa792 100644 --- a/app/assets/stylesheets/framework/jquery.scss +++ b/app/assets/stylesheets/framework/jquery.scss @@ -1,5 +1,5 @@ .ui-widget { - font-family: $regular_font; + font-family: $regular-font; font-size: $font-size-base; .ui-state-default { diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index d54490c87c68d2e0f02d4085d0897357169005f0..4b67eab05b3bf4777102efcbb1ae9b9f30c50585 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -259,7 +259,7 @@ ul.controls { margin-right: 0; } - .author_link { + .author-link { .avatar-inline { margin-left: 0; margin-right: 0; @@ -270,7 +270,7 @@ ul.controls { .issuable-pipeline-broken a, .issuable-pipeline-status a, - .author_link { + .author-link { display: flex; } } diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 76ebfc22ef760fd053a7f607922a30956e47eaf7..11d332be1beb1447e80a77b1b1c4a2c8bcd4e808 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -3,13 +3,13 @@ * Mixins with fixed values */ -@mixin str-truncated($max_width: 82%) { +@mixin str-truncated($max-width: 82%) { display: inline-block; overflow: hidden; text-overflow: ellipsis; vertical-align: top; white-space: nowrap; - max-width: $max_width; + max-width: $max-width; } /* diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 8c7164009132efc04ba8b155853a3df9aa1e786e..c4dbcf2ddc933bc6d1ee9f857e0f1123e4530b8b 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -33,11 +33,11 @@ @include media-breakpoint-up(sm) { &:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper { - padding-right: $gutter_collapsed_width; + padding-right: $gutter-collapsed-width; } .merge-request-tabs-holder.affix { - right: $gutter_collapsed_width; + right: $gutter-collapsed-width; } } @@ -67,21 +67,21 @@ @include media-breakpoint-only(sm) { &:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper { - padding-right: $gutter_collapsed_width; + padding-right: $gutter-collapsed-width; } } @include media-breakpoint-up(md) { .content-wrapper { - padding-right: $gutter_width; + padding-right: $gutter-width; } &:not(.with-overlay) .merge-request-tabs-holder.affix { - right: $gutter_width; + right: $gutter-width; } &.with-overlay .merge-request-tabs-holder.affix { - right: $gutter_collapsed_width; + right: $gutter-collapsed-width; } } } diff --git a/app/assets/stylesheets/framework/stacked_progress_bar.scss b/app/assets/stylesheets/framework/stacked_progress_bar.scss index 528ba53a48b293f0c156475cab1f2f595efaa9fa..29a2d5881f77e6fac579cbff445482ab57d3c74a 100644 --- a/app/assets/stylesheets/framework/stacked_progress_bar.scss +++ b/app/assets/stylesheets/framework/stacked_progress_bar.scss @@ -10,7 +10,7 @@ .status-neutral, .status-red, { height: 100%; - min-width: 30px; + min-width: 40px; padding: 0 5px; font-size: $tooltip-font-size; font-weight: normal; diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 9874c92860432ab96e80e98beed5f5fdeb287d57..56307777a72955cd28062be1f9d7c26856cf6d69 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -44,7 +44,7 @@ // Single code lines should wrap code { - font-family: $monospace_font; + font-family: $monospace-font; white-space: pre-wrap; word-wrap: normal; } @@ -321,7 +321,7 @@ h6 { /** CODE **/ pre { - font-family: $monospace_font; + font-family: $monospace-font; display: block; padding: $gl-padding-8; margin: 0 0 $gl-padding-8; @@ -342,7 +342,7 @@ code { } .monospace { - font-family: $monospace_font; + font-family: $monospace-font; } .weight-normal { @@ -381,7 +381,7 @@ code { * */ textarea.js-gfm-input { - font-family: $monospace_font; + font-family: $monospace-font; font-size: 13px; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 6c2fdbe0608eadcb6d9bf35592233de9243b1892..efc54196b75f72238bc45089ed5f511fe2112254 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -2,9 +2,9 @@ * Layout */ $grid-size: 8px; -$gutter_collapsed_width: 62px; -$gutter_width: 290px; -$gutter_inner_width: 250px; +$gutter-collapsed-width: 62px; +$gutter-width: 290px; +$gutter-inner-width: 250px; $sidebar-transition-duration: 0.3s; $sidebar-breakpoint: 1024px; $default-transition-duration: 0.15s; @@ -233,8 +233,8 @@ $md-area-border: #ddd; /* * Code */ -$code_font_size: 90%; -$code_line_height: 1.6; +$code-font-size: 90%; +$code-line-height: 1.6; /* * Tooltips @@ -371,9 +371,9 @@ $diff-jagged-border-gradient-color: darken($white-normal, 8%); /* * Fonts */ -$monospace_font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono', +$monospace-font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace; -$regular_font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, +$regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; /* @@ -526,7 +526,7 @@ $issue-board-list-difference-md: $issue-board-list-difference-sm + $issue-boards /* * Avatar */ -$avatar_radius: 50%; +$avatar-radius: 50%; $avatar-border: $gray-normal; $avatar-border-hover: $gray-darker; $avatar-background: $gray-lightest; @@ -830,8 +830,8 @@ $secondary: $gray-light; $input-disabled-bg: $gray-light; $input-border-color: $theme-gray-200; $input-color: $gl-text-color; -$font-family-sans-serif: $regular_font; -$font-family-monospace: $monospace_font; +$font-family-sans-serif: $regular-font; +$font-family-monospace: $monospace-font; $input-line-height: 20px; $btn-line-height: 20px; $table-accent-bg: $gray-light; diff --git a/app/assets/stylesheets/mailers/highlighted_diff_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss index 1835c4364d3f3cc8312ebc7f2667a2ac8b09fbd4..8b234a5a65699da6ed0b9f8f1e1de810e823926e 100644 --- a/app/assets/stylesheets/mailers/highlighted_diff_email.scss +++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss @@ -77,13 +77,13 @@ $highlighted-gc-bg: #eaf2f5; .code { background-color: $white-light; font-family: monospace; - font-size: $code_font_size; + font-size: $code-font-size; -premailer-cellpadding: 0; -premailer-cellspacing: 0; -premailer-width: 100%; > tr { - line-height: $code_line_height; + line-height: $code-line-height; } } diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 5de53892fac5598f7bfc29f38e116d7e396ef450..7347da2ae6160f2c7f5262f97fb8c6727160812f 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -63,7 +63,7 @@ width: 100%; &.is-compact { - width: calc(100% - #{$gutter_width}); + width: calc(100% - #{$gutter-width}); } } } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index f75be4e01cd380a03b552e55ed11ca4bb700f2c0..9b51c54a0fcb8b50261611bc7af7f3b564f01b56 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -79,7 +79,7 @@ .commit-message-container { background-color: $body-bg; position: relative; - font-family: $monospace_font; + font-family: $monospace-font; $left: 12px; overflow: hidden; // See https://gitlab.com/gitlab-org/gitlab-ce/issues/13987 .max-width-marker { @@ -205,7 +205,7 @@ > .ci-status-link, > .btn, > .commit-sha-group { - margin-left: $gl-padding-8; + margin-left: $gl-padding; } } @@ -235,10 +235,6 @@ fill: $gl-text-color-secondary; } - .fa-clipboard { - color: $gl-text-color-secondary; - } - :first-child { border-bottom-left-radius: $border-radius-default; border-top-left-radius: $border-radius-default; diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index a22c666a525478de952bafe19f7a93875132f8db..e2c0a7a622587eb745db38308a04647b0413f160 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -368,7 +368,7 @@ .fa { color: $gl-text-color-secondary; - font-size: $code_font_size; + font-size: $code-font-size; } } } diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss index 2e007c525924997503c6b655e2f699cd93add7b7..37ed5ae674a484e53e6c5f8d6983491ef5760045 100644 --- a/app/assets/stylesheets/pages/detail_page.scss +++ b/app/assets/stylesheets/pages/detail_page.scss @@ -10,7 +10,7 @@ } .issue_created_ago, - .author_link { + .author-link { white-space: nowrap; } diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 5e39bbb9890c15e8a26851a465ede5a9b5309337..b616357bb8d0b03f750f56e610d617d1f7b49730 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -56,7 +56,7 @@ table { width: 100%; - font-family: $monospace_font; + font-family: $monospace-font; border: 0; border-collapse: separate; margin: 0; @@ -73,8 +73,8 @@ } .line_holder td { - line-height: $code_line_height; - font-size: $code_font_size; + line-height: $code-line-height; + font-size: $code-font-size; &.noteable_line { position: relative; diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 437621299e037b46e51b9c26ca580a8d7bb6e5fd..ddd1f8cc98a50a3af9668a3f48e03377bd95eecb 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -84,7 +84,7 @@ .soft-wrap-toggle { display: inline-block; vertical-align: top; - font-family: $regular_font; + font-family: $regular-font; } .soft-wrap-toggle { diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 8915b323b3c012594a8b6c770d3c3017dbbf0357..8a07401734490057c6db72fbecb0feb3e4d46b05 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -478,7 +478,7 @@ } .deploy-info-text-link { - font-family: $monospace_font; + font-family: $monospace-font; fill: $gl-link-color; &:hover { diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index f9fd9f1ab8b4523c3d38b3fc42308c9c39413f2c..797b106de23b3dde7bf9dd150479a36fd1cf842b 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -166,7 +166,7 @@ border-bottom: 1px solid $border-gray-normal; // This prevents the mess when resizing the sidebar // of elements repositioning themselves.. - width: $gutter_inner_width; + width: $gutter-inner-width; // -- &.issuable-sidebar-header { @@ -197,7 +197,7 @@ } &.assignee { - .author_link { + .author-link { display: block; padding-left: 42px; position: relative; @@ -290,7 +290,7 @@ } &.right-sidebar-expanded { - width: $gutter_width; + width: $gutter-width; .value { line-height: 1; @@ -377,11 +377,11 @@ display: block; } - width: $gutter_collapsed_width; + width: $gutter-collapsed-width; padding: 0; .block { - width: $gutter_collapsed_width - 2px; + width: $gutter-collapsed-width - 2px; padding: 15px 0 0; border-bottom: 0; overflow: hidden; @@ -486,7 +486,7 @@ padding-bottom: 0; margin-bottom: 10px; - .author_link { + .author-link { padding-left: 0; .avatar { @@ -595,7 +595,7 @@ margin: 16px 0 0; font-size: 85%; - .author_link { + .author-link { color: $gray-darkest; } } @@ -620,7 +620,7 @@ padding-right: 0; } - .author_link { + .author-link { display: block; } diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 19fb99bfa93962b5970d60abf3d3c4a72c74e2d4..212e5979273801b053a8d18b3ecb1b5b8107dc84 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -12,7 +12,7 @@ } .issuable-meta { - .author_link { + .author-link { display: inline-block; } diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss index e76525fdbf62852bef2a71e9cb41fa409ec17098..d26659701e19e05066af25c7ab481a13031aaee2 100644 --- a/app/assets/stylesheets/pages/merge_conflicts.scss +++ b/app/assets/stylesheets/pages/merge_conflicts.scss @@ -1,167 +1,162 @@ // Disabled to use the color map for creating color schemes // scss-lint:disable ColorVariable $colors: ( - white_header_head_neutral : #e1fad7, - white_line_head_neutral : #effdec, - white_button_head_neutral : #9adb84, + white-header-head-neutral : #e1fad7, + white-line-head-neutral : #effdec, + white-button-head-neutral : #9adb84, - white_header_head_chosen : #baf0a8, - white_line_head_chosen : #e1fad7, - white_button_head_chosen : #52c22d, + white-header-head-chosen : #baf0a8, + white-line-head-chosen : #e1fad7, + white-button-head-chosen : #52c22d, - white_header_origin_neutral : #e0f0ff, - white_line_origin_neutral : #f2f9ff, - white_button_origin_neutral : #87c2fa, + white-header-origin-neutral : #e0f0ff, + white-line-origin-neutral : #f2f9ff, + white-button-origin-neutral : #87c2fa, - white_header_origin_chosen : #add8ff, - white_line_origin_chosen : #e0f0ff, - white_button_origin_chosen : #268ced, + white-header-origin-chosen : #add8ff, + white-line-origin-chosen : #e0f0ff, + white-button-origin-chosen : #268ced, - white_header_not_chosen : #f0f0f0, - white_line_not_chosen : $gray-light, + white-header-not-chosen : #f0f0f0, + white-line-not-chosen : $gray-light, + dark-header-head-neutral : rgba(#3f3, .2), + dark-line-head-neutral : rgba(#3f3, .1), + dark-button-head-neutral : #40874f, - dark_header_head_neutral : rgba(#3f3, .2), - dark_line_head_neutral : rgba(#3f3, .1), - dark_button_head_neutral : #40874f, + dark-header-head-chosen : rgba(#3f3, .33), + dark-line-head-chosen : rgba(#3f3, .2), + dark-button-head-chosen : #258537, - dark_header_head_chosen : rgba(#3f3, .33), - dark_line_head_chosen : rgba(#3f3, .2), - dark_button_head_chosen : #258537, + dark-header-origin-neutral : rgba(#2878c9, .4), + dark-line-origin-neutral : rgba(#2878c9, .3), + dark-button-origin-neutral : #2a5c8c, - dark_header_origin_neutral : rgba(#2878c9, .4), - dark_line_origin_neutral : rgba(#2878c9, .3), - dark_button_origin_neutral : #2a5c8c, + dark-header-origin-chosen : rgba(#2878c9, .6), + dark-line-origin-chosen : rgba(#2878c9, .4), + dark-button-origin-chosen : #1d6cbf, - dark_header_origin_chosen : rgba(#2878c9, .6), - dark_line_origin_chosen : rgba(#2878c9, .4), - dark_button_origin_chosen : #1d6cbf, + dark-header-not-chosen : rgba(#fff, .25), + dark-line-not-chosen : rgba(#fff, .1), - dark_header_not_chosen : rgba(#fff, .25), - dark_line_not_chosen : rgba(#fff, .1), + monokai-header-head-neutral : rgba(#a6e22e, .25), + monokai-line-head-neutral : rgba(#a6e22e, .1), + monokai-button-head-neutral : #376b20, + monokai-header-head-chosen : rgba(#a6e22e, .4), + monokai-line-head-chosen : rgba(#a6e22e, .25), + monokai-button-head-chosen : #39800d, - monokai_header_head_neutral : rgba(#a6e22e, .25), - monokai_line_head_neutral : rgba(#a6e22e, .1), - monokai_button_head_neutral : #376b20, + monokai-header-origin-neutral : rgba(#60d9f1, .35), + monokai-line-origin-neutral : rgba(#60d9f1, .15), + monokai-button-origin-neutral : #38848c, - monokai_header_head_chosen : rgba(#a6e22e, .4), - monokai_line_head_chosen : rgba(#a6e22e, .25), - monokai_button_head_chosen : #39800d, + monokai-header-origin-chosen : rgba(#60d9f1, .5), + monokai-line-origin-chosen : rgba(#60d9f1, .35), + monokai-button-origin-chosen : #3ea4b2, - monokai_header_origin_neutral : rgba(#60d9f1, .35), - monokai_line_origin_neutral : rgba(#60d9f1, .15), - monokai_button_origin_neutral : #38848c, + monokai-header-not-chosen : rgba(#76715d, .24), + monokai-line-not-chosen : rgba(#76715d, .1), - monokai_header_origin_chosen : rgba(#60d9f1, .5), - monokai_line_origin_chosen : rgba(#60d9f1, .35), - monokai_button_origin_chosen : #3ea4b2, + solarized-light-header-head-neutral : rgba(#859900, .37), + solarized-light-line-head-neutral : rgba(#859900, .2), + solarized-light-button-head-neutral : #afb262, - monokai_header_not_chosen : rgba(#76715d, .24), - monokai_line_not_chosen : rgba(#76715d, .1), + solarized-light-header-head-chosen : rgba(#859900, .5), + solarized-light-line-head-chosen : rgba(#859900, .37), + solarized-light-button-head-chosen : #94993d, + solarized-light-header-origin-neutral : rgba(#2878c9, .37), + solarized-light-line-origin-neutral : rgba(#2878c9, .15), + solarized-light-button-origin-neutral : #60a1bf, - solarized_light_header_head_neutral : rgba(#859900, .37), - solarized_light_line_head_neutral : rgba(#859900, .2), - solarized_light_button_head_neutral : #afb262, + solarized-light-header-origin-chosen : rgba(#2878c9, .6), + solarized-light-line-origin-chosen : rgba(#2878c9, .37), + solarized-light-button-origin-chosen : #2482b2, - solarized_light_header_head_chosen : rgba(#859900, .5), - solarized_light_line_head_chosen : rgba(#859900, .37), - solarized_light_button_head_chosen : #94993d, + solarized-light-header-not-chosen : rgba(#839496, .37), + solarized-light-line-not-chosen : rgba(#839496, .2), - solarized_light_header_origin_neutral : rgba(#2878c9, .37), - solarized_light_line_origin_neutral : rgba(#2878c9, .15), - solarized_light_button_origin_neutral : #60a1bf, + solarized-dark-header-head-neutral : rgba(#859900, .35), + solarized-dark-line-head-neutral : rgba(#859900, .15), + solarized-dark-button-head-neutral : #376b20, - solarized_light_header_origin_chosen : rgba(#2878c9, .6), - solarized_light_line_origin_chosen : rgba(#2878c9, .37), - solarized_light_button_origin_chosen : #2482b2, + solarized-dark-header-head-chosen : rgba(#859900, .5), + solarized-dark-line-head-chosen : rgba(#859900, .35), + solarized-dark-button-head-chosen : #39800d, - solarized_light_header_not_chosen : rgba(#839496, .37), - solarized_light_line_not_chosen : rgba(#839496, .2), + solarized-dark-header-origin-neutral : rgba(#2878c9, .35), + solarized-dark-line-origin-neutral : rgba(#2878c9, .15), + solarized-dark-button-origin-neutral : #086799, + solarized-dark-header-origin-chosen : rgba(#2878c9, .6), + solarized-dark-line-origin-chosen : rgba(#2878c9, .35), + solarized-dark-button-origin-chosen : #0082cc, - solarized_dark_header_head_neutral : rgba(#859900, .35), - solarized_dark_line_head_neutral : rgba(#859900, .15), - solarized_dark_button_head_neutral : #376b20, - - solarized_dark_header_head_chosen : rgba(#859900, .5), - solarized_dark_line_head_chosen : rgba(#859900, .35), - solarized_dark_button_head_chosen : #39800d, - - solarized_dark_header_origin_neutral : rgba(#2878c9, .35), - solarized_dark_line_origin_neutral : rgba(#2878c9, .15), - solarized_dark_button_origin_neutral : #086799, - - solarized_dark_header_origin_chosen : rgba(#2878c9, .6), - solarized_dark_line_origin_chosen : rgba(#2878c9, .35), - solarized_dark_button_origin_chosen : #0082cc, - - solarized_dark_header_not_chosen : rgba(#839496, .25), - solarized_dark_line_not_chosen : rgba(#839496, .15) + solarized-dark-header-not-chosen : rgba(#839496, .25), + solarized-dark-line-not-chosen : rgba(#839496, .15) ); // scss-lint:enable ColorVariable - @mixin color-scheme($color) { .header.line_content, .diff-line-num { &.origin { - background-color: map-get($colors, #{$color}_header_origin_neutral); - border-color: map-get($colors, #{$color}_header_origin_neutral); + background-color: map-get($colors, #{$color}-header-origin-neutral); + border-color: map-get($colors, #{$color}-header-origin-neutral); button { - background-color: map-get($colors, #{$color}_button_origin_neutral); - border-color: darken(map-get($colors, #{$color}_button_origin_neutral), 15); + background-color: map-get($colors, #{$color}-button-origin-neutral); + border-color: darken(map-get($colors, #{$color}-button-origin-neutral), 15); } &.selected { - background-color: map-get($colors, #{$color}_header_origin_chosen); - border-color: map-get($colors, #{$color}_header_origin_chosen); + background-color: map-get($colors, #{$color}-header-origin-chosen); + border-color: map-get($colors, #{$color}-header-origin-chosen); button { - background-color: map-get($colors, #{$color}_button_origin_chosen); - border-color: darken(map-get($colors, #{$color}_button_origin_chosen), 15); + background-color: map-get($colors, #{$color}-button-origin-chosen); + border-color: darken(map-get($colors, #{$color}-button-origin-chosen), 15); } } &.unselected { - background-color: map-get($colors, #{$color}_header_not_chosen); - border-color: map-get($colors, #{$color}_header_not_chosen); + background-color: map-get($colors, #{$color}-header-not-chosen); + border-color: map-get($colors, #{$color}-header-not-chosen); button { - background-color: lighten(map-get($colors, #{$color}_button_origin_neutral), 15); - border-color: map-get($colors, #{$color}_button_origin_neutral); + background-color: lighten(map-get($colors, #{$color}-button-origin-neutral), 15); + border-color: map-get($colors, #{$color}-button-origin-neutral); } } } &.head { - background-color: map-get($colors, #{$color}_header_head_neutral); - border-color: map-get($colors, #{$color}_header_head_neutral); + background-color: map-get($colors, #{$color}-header-head-neutral); + border-color: map-get($colors, #{$color}-header-head-neutral); button { - background-color: map-get($colors, #{$color}_button_head_neutral); - border-color: darken(map-get($colors, #{$color}_button_head_neutral), 15); + background-color: map-get($colors, #{$color}-button-head-neutral); + border-color: darken(map-get($colors, #{$color}-button-head-neutral), 15); } &.selected { - background-color: map-get($colors, #{$color}_header_head_chosen); - border-color: map-get($colors, #{$color}_header_head_chosen); + background-color: map-get($colors, #{$color}-header-head-chosen); + border-color: map-get($colors, #{$color}-header-head-chosen); button { - background-color: map-get($colors, #{$color}_button_head_chosen); - border-color: darken(map-get($colors, #{$color}_button_head_chosen), 15); + background-color: map-get($colors, #{$color}-button-head-chosen); + border-color: darken(map-get($colors, #{$color}-button-head-chosen), 15); } } &.unselected { - background-color: map-get($colors, #{$color}_header_not_chosen); - border-color: map-get($colors, #{$color}_header_not_chosen); + background-color: map-get($colors, #{$color}-header-not-chosen); + border-color: map-get($colors, #{$color}-header-not-chosen); button { - background-color: lighten(map-get($colors, #{$color}_button_head_neutral), 15); - border-color: map-get($colors, #{$color}_button_head_neutral); + background-color: lighten(map-get($colors, #{$color}-button-head-neutral), 15); + border-color: map-get($colors, #{$color}-button-head-neutral); } } } @@ -169,32 +164,31 @@ $colors: ( .line_content { &.origin { - background-color: map-get($colors, #{$color}_line_origin_neutral); + background-color: map-get($colors, #{$color}-line-origin-neutral); &.selected { - background-color: map-get($colors, #{$color}_line_origin_chosen); + background-color: map-get($colors, #{$color}-line-origin-chosen); } &.unselected { - background-color: map-get($colors, #{$color}_line_not_chosen); + background-color: map-get($colors, #{$color}-line-not-chosen); } } &.head { - background-color: map-get($colors, #{$color}_line_head_neutral); + background-color: map-get($colors, #{$color}-line-head-neutral); &.selected { - background-color: map-get($colors, #{$color}_line_head_chosen); + background-color: map-get($colors, #{$color}-line-head-chosen); } &.unselected { - background-color: map-get($colors, #{$color}_line_not_chosen); + background-color: map-get($colors, #{$color}-line-not-chosen); } } } } - #conflicts { .white { @@ -210,11 +204,11 @@ $colors: ( } .solarized-light { - @include color-scheme('solarized_light') + @include color-scheme('solarized-light') } .solarized-dark { - @include color-scheme('solarized_dark') + @include color-scheme('solarized-dark') } .diff-wrap-lines .line_content { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index c8349a4ef79dea305f97c3594951792bf6bccb5e..7bd0f0bf1e0bd9349e99979f7cbccc6a879437a3 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -208,7 +208,7 @@ position: absolute; content: '...'; right: 0; - font-family: $regular_font; + font-family: $regular-font; background-color: $gray-light; } } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 5e5696b16023f0ea7eec7b0b942cb0529232cd80..dcf590e7331969432d3474843a8c28031a5fd7c4 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -42,7 +42,7 @@ display: block; padding: 10px 0; color: $gl-text-color; - font-family: $regular_font; + font-family: $regular-font; border: 0; &:focus { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 32d140490676638649b8625f5a30da834b07aa92..7fc2936c5e6773de80f925b3c3de664a0f6654b3 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -328,7 +328,7 @@ ul.notes { } .notes_holder { - font-family: $regular_font; + font-family: $regular-font; td { border: 1px solid $white-normal; @@ -403,7 +403,7 @@ ul.notes { } } - .author_link { + .author-link { color: $gl-text-color; } } diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 8b1227b9131b4c3510f66370777b35fc9e75dd22..2d76f0ce0049dba73175863a05725a34ebb95200 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -961,7 +961,7 @@ overflow: hidden; .note-textarea { - font-family: $monospace_font; + font-family: $monospace-font; } } diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss index 0d6b0735f700d876d12de1b6b9d90308d2e865d2..64110f9c3a0d546d92c1d74bd34d43eb17e32d48 100644 --- a/app/assets/stylesheets/snippets.scss +++ b/app/assets/stylesheets/snippets.scss @@ -6,9 +6,9 @@ $border-style: 1px solid $border-color; - font-family: $regular_font; + font-family: $regular-font; font-size: $gl-font-size; - line-height: $code_line_height; + line-height: $code-line-height; color: $gl-text-color; margin: 20px; font-weight: 200; @@ -48,9 +48,9 @@ padding: 10px; border: 0; border-radius: 0; - font-family: $monospace_font; - font-size: $code_font_size; - line-height: $code_line_height; + font-family: $monospace-font; + font-size: $code-font-size; + line-height: $code-line-height; margin: 0; overflow: auto; overflow-y: hidden; @@ -66,10 +66,10 @@ float: left; .diff-line-num { - font-family: $monospace_font; + font-family: $monospace-font; display: block; - font-size: $code_font_size; - min-height: $code_line_height; + font-size: $code-font-size; + min-height: $code-line-height; white-space: nowrap; color: $black-transparent; min-width: 30px; diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb index 79ee5b2f91ec5974ba944088663867058da7bcdb..4584ff782a370d5cd3c73973a241aa644cbabfb6 100644 --- a/app/controllers/concerns/lfs_request.rb +++ b/app/controllers/concerns/lfs_request.rb @@ -71,7 +71,22 @@ module LfsRequest def lfs_download_access? return false unless project.lfs_enabled? - ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code? + ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code? || deploy_token_can_download_code? + end + + def deploy_token_can_download_code? + deploy_token_present? && + deploy_token.project == project && + deploy_token.active? && + deploy_token.read_repository? + end + + def deploy_token_present? + user && user.is_a?(DeployToken) + end + + def deploy_token + user end def lfs_upload_access? @@ -86,7 +101,7 @@ module LfsRequest end def user_can_download_code? - has_authentication_ability?(:download_code) && can?(user, :download_code, project) + has_authentication_ability?(:download_code) && can?(user, :download_code, project) && !deploy_token_present? end def build_can_download_code? diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb index fccbdbca0f61912fb5cd7c845738c5370964e702..53f70446d95dd946c73c89beef99b53bbc642dd5 100644 --- a/app/controllers/import/gitlab_controller.rb +++ b/app/controllers/import/gitlab_controller.rb @@ -1,4 +1,7 @@ class Import::GitlabController < Import::BaseController + MAX_PROJECT_PAGES = 15 + PER_PAGE_PROJECTS = 100 + before_action :verify_gitlab_import_enabled before_action :gitlab_auth, except: :callback @@ -10,7 +13,7 @@ class Import::GitlabController < Import::BaseController end def status - @repos = client.projects + @repos = client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS) @already_added_projects = find_already_added_projects('gitlab') already_added_projects_names = @already_added_projects.pluck(:import_source) diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 074db361949391b00ec06b9e72d91b56f9332f47..56a7b766b77a1cc0b5a99fe33101931e44e7e6f8 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -99,7 +99,8 @@ class ProfilesController < Profiles::ApplicationController :username, :website_url, :organization, - :preferred_language + :preferred_language, + :private_profile ) end end diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index 9dc0c31be49a9fd270ea1773005eb88fe77cdcee..b7c656246ef1db3b478477897e0237f0ee8801e3 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -112,7 +112,7 @@ class Projects::WikisController < Projects::ApplicationController private def load_project_wiki - @project_wiki = ProjectWiki.new(@project, current_user) + @project_wiki = load_wiki # Call #wiki to make sure the Wiki Repo is initialized @project_wiki.wiki @@ -128,6 +128,10 @@ class Projects::WikisController < Projects::ApplicationController false end + def load_wiki + ProjectWiki.new(@project, current_user) + end + def wiki_params params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha) end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 9dd652206fe8a5c8848ee6ba0731a6959aa26527..4ca42e2d4a20e0f63d15355aaa588b22d72c1348 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -157,6 +157,8 @@ class SessionsController < Devise::SessionsController end def auto_sign_in_with_provider + return unless Gitlab::Auth.omniauth_enabled? + provider = Gitlab.config.omniauth.auto_sign_in_with_provider return unless provider.present? diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 31f47a7aa7c21a3bc12b3d0b1d7a410d24919cc8..2f65f4a7403426ab5f60f3926a194cb829ff9969 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -13,6 +13,8 @@ class UsersController < ApplicationController skip_before_action :authenticate_user! before_action :user, except: [:exists] + before_action :authorize_read_user_profile!, + only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :snippets] def show respond_to do |format| @@ -148,4 +150,8 @@ class UsersController < ApplicationController def build_canonical_path(user) url_for(safe_params.merge(username: user.to_param)) end + + def authorize_read_user_profile! + access_denied! unless can?(current_user, :read_user_profile, user) + end end diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb index 0754123a3cfbfd1f54ec79dea7a86857ed33d185..0eeba1d2428a656018a9d44063a6438776e89d6f 100644 --- a/app/finders/groups_finder.rb +++ b/app/finders/groups_finder.rb @@ -8,6 +8,7 @@ # owned: boolean # parent: Group # all_available: boolean (defaults to true) +# min_access_level: integer # # Users with full private access can see all groups. The `owned` and `parent` # params can be used to restrict the groups that are returned. @@ -39,6 +40,7 @@ class GroupsFinder < UnionFinder def all_groups return [owned_groups] if params[:owned] + return [groups_with_min_access_level] if min_access_level? return [Group.all] if current_user&.full_private_access? && all_available? groups = [] @@ -56,6 +58,16 @@ class GroupsFinder < UnionFinder current_user.groups end + def groups_with_min_access_level + groups = current_user + .groups + .where('members.access_level >= ?', params[:min_access_level]) + + Gitlab::GroupHierarchy + .new(groups) + .base_and_descendants + end + def by_parent(groups) return groups unless params[:parent] @@ -73,4 +85,8 @@ class GroupsFinder < UnionFinder def all_available? params.fetch(:all_available, true) end + + def min_access_level? + current_user && params[:min_access_level].present? + end end diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb index 5aea0cb81928a061c2cfd53c6ec703d73ca9d229..a56a3a1e1a9bdb1ed928fc4e495bb8ba8e00b33c 100644 --- a/app/finders/personal_projects_finder.rb +++ b/app/finders/personal_projects_finder.rb @@ -1,6 +1,9 @@ class PersonalProjectsFinder < UnionFinder - def initialize(user) + include Gitlab::Allowable + + def initialize(user, params = {}) @user = user + @params = params end # Finds the projects belonging to the user in "@user", limited to either @@ -8,9 +11,13 @@ class PersonalProjectsFinder < UnionFinder # # current_user - When given the list of projects is limited to those only # visible by this user. + # params - Optional query parameters + # min_access_level: integer # # Returns an ActiveRecord::Relation. def execute(current_user = nil) + return Project.none unless can?(current_user, :read_user_profile, @user) + segments = all_projects(current_user) find_union(segments, Project).includes(:namespace).order_updated_desc @@ -19,11 +26,21 @@ class PersonalProjectsFinder < UnionFinder private def all_projects(current_user) - projects = [] + return [projects_with_min_access_level(current_user)] if current_user && min_access_level? + projects = [] projects << @user.personal_projects.visible_to_user(current_user) if current_user projects << @user.personal_projects.public_to_user(current_user) - projects end + + def projects_with_min_access_level(current_user) + @user + .personal_projects + .visible_to_user_and_access_level(current_user, @params[:min_access_level]) + end + + def min_access_level? + @params[:min_access_level].present? + end end diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index b06595081e7f8b5f2e7f8e083a3832273caf21d6..cac6643eff3a71f4f6098107c1e877767d6ac412 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -17,6 +17,7 @@ # search: string # non_archived: boolean # archived: 'only' or boolean +# min_access_level: integer # class ProjectsFinder < UnionFinder include CustomAttributesFilter @@ -34,7 +35,7 @@ class ProjectsFinder < UnionFinder user = params.delete(:user) collection = if user - PersonalProjectsFinder.new(user).execute(current_user) + PersonalProjectsFinder.new(user, finder_params).execute(current_user) else init_collection end @@ -65,6 +66,8 @@ class ProjectsFinder < UnionFinder def collection_with_user if owned_projects? current_user.owned_projects + elsif min_access_level? + current_user.authorized_projects.where('project_authorizations.access_level >= ?', params[:min_access_level]) else if private_only? current_user.authorized_projects @@ -76,7 +79,7 @@ class ProjectsFinder < UnionFinder # Builds a collection for an anonymous user. def collection_without_user - if private_only? || owned_projects? + if private_only? || owned_projects? || min_access_level? Project.none else Project.public_to_user @@ -91,6 +94,10 @@ class ProjectsFinder < UnionFinder params[:non_public].present? end + def min_access_level? + params[:min_access_level].present? + end + def by_ids(items) project_ids_relation ? items.where(id: project_ids_relation) : items end @@ -143,4 +150,10 @@ class ProjectsFinder < UnionFinder projects end end + + def finder_params + return {} unless min_access_level? + + { min_access_level: params[:min_access_level] } + end end diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb index 74776b2ed1f1cfa1f3880476150e90e0ea82d0fa..876f086a3ef0d02dbbd74b7bb2a5982e8a91da6d 100644 --- a/app/finders/user_recent_events_finder.rb +++ b/app/finders/user_recent_events_finder.rb @@ -7,6 +7,7 @@ class UserRecentEventsFinder prepend FinderWithCrossProjectAccess include FinderMethods + include Gitlab::Allowable requires_cross_project_access @@ -21,6 +22,8 @@ class UserRecentEventsFinder end def execute + return Event.none unless can?(current_user, :read_user_profile, target_user) + recent_events(params[:offset] || 0) .joins(:project) .with_associations diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index d2daee22aba4fd09fcf85611b2f0cebdd5cfdb1c..18f0979fc86355a8b3394de3c6faedea525cc0f8 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -7,7 +7,7 @@ module AuthHelper end def omniauth_enabled? - Gitlab.config.omniauth.enabled + Gitlab::Auth.omniauth_enabled? end def provider_has_icon?(name) diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index 3605d6a3c959e875eeb64cc3dbee503e4b2c65de..0171a8801648c514478705bd8c7da3a78fea7e82 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -51,7 +51,7 @@ module ButtonHelper } content_tag :button, button_attributes do - concat(icon('clipboard', 'aria-hidden': 'true')) unless hide_button_icon + concat(sprite_icon('duplicate')) unless hide_button_icon concat(button_text) end end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index f49b5c7b51abb53ca981cf0419bbdd083406d897..330959e536dc1f5983c06fe8cafc3fef661ae4de 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -56,7 +56,7 @@ module CiStatusHelper status.humanize end - def ci_icon_for_status(status) + def ci_icon_for_status(status, size: 16) if detailed_status?(status) return sprite_icon(status.icon) end @@ -85,7 +85,7 @@ module CiStatusHelper 'status_canceled' end - sprite_icon(icon_name, size: 16) + sprite_icon(icon_name, size: size) end def pipeline_status_cache_key(pipeline_status) @@ -111,7 +111,8 @@ module CiStatusHelper 'commit', commit.status(ref), path, - tooltip_placement: tooltip_placement) + tooltip_placement: tooltip_placement, + icon_size: 24) end def render_pipeline_status(pipeline, tooltip_placement: 'left') @@ -125,16 +126,16 @@ module CiStatusHelper Ci::Runner.instance_type.blank? end - def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body') + def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16) klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" data = { toggle: 'tooltip', placement: tooltip_placement, container: container } if path - link_to ci_icon_for_status(status), path, + link_to ci_icon_for_status(status, size: icon_size), path, class: klass, title: title, data: data else - content_tag :span, ci_icon_for_status(status), + content_tag :span, ci_icon_for_status(status, size: icon_size), class: klass, title: title, data: data end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index e5c3be47801b56ae1987ca947ae765c41de0e1d1..89fe90fd801d751e678b997dd101fec4c60f96f1 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -145,15 +145,14 @@ module CommitsHelper person_name end - options = { - class: "commit-#{options[:source]}-link has-tooltip", - title: source_email + link_options = { + class: "commit-#{options[:source]}-link" } if user.nil? - mail_to(source_email, text, options) + mail_to(source_email, text, link_options) else - link_to(text, user_path(user), options) + link_to(text, user_path(user), link_options) end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 221f1aa9dd8a74c72cc1828791a34dd99b6c3e01..aaf9dff43ee88b7f16b9fe61b847c8634fdee09f 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -63,10 +63,10 @@ module ProjectsHelper author_html = author_html.html_safe if opts[:name] - link_to(author_html, user_path(author), class: "author_link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe + link_to(author_html, user_path(author), class: "author-link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe else title = opts[:title].sub(":name", sanitize(author.name)) - link_to(author_html, user_path(author), class: "author_link has-tooltip", title: title, data: { container: 'body' }).html_safe + link_to(author_html, user_path(author), class: "author-link has-tooltip", title: title, data: { container: 'body' }).html_safe end end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 4d17b22a4a1eae33b83e8bcb908268c40144a79a..8ee4203b6f5cb43ff555849f72298d5449f0e6b9 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -42,7 +42,13 @@ module UsersHelper private def get_profile_tabs - [:activity, :groups, :contributed, :projects, :snippets] + tabs = [] + + if can?(current_user, :read_user_profile, @user) + tabs += [:activity, :groups, :contributed, :projects, :snippets] + end + + tabs end def get_current_user_menu_items diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index e395cda03d387a3db733e19acce70d526628bb6b..cf2fe5a2019dc7c07975c870336dad58a3b9fdf0 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -126,10 +126,9 @@ module VisibilityLevelHelper end def visibility_icon_description(form_model) - case form_model - when Project + if form_model.respond_to?(:visibility_level_allowed_as_fork?) project_visibility_icon_description(form_model.visibility_level) - when Group + elsif form_model.respond_to?(:visibility_level_allowed_by_sub_groups?) group_visibility_icon_description(form_model.visibility_level) end end diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb index 5082dc45368dc22cc68741d7d31182dce1b0f3bb..7ab647abe9333fb48d74614c038678647ab0b92f 100644 --- a/app/models/deploy_token.rb +++ b/app/models/deploy_token.rb @@ -27,7 +27,7 @@ class DeployToken < ActiveRecord::Base end def active? - !revoked + !revoked && expires_at > Date.today end def scopes @@ -58,6 +58,10 @@ class DeployToken < ActiveRecord::Base write_attribute(:expires_at, value.presence || Forever.date) end + def admin? + false + end + private def ensure_at_least_one_scope diff --git a/app/models/project.rb b/app/models/project.rb index 5f582dfa5ee1a53613b347472efff6e70a4f43d1..325dbd0197ffb1535d4a7136239cc371e1daf992 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -154,6 +154,7 @@ class Project < ActiveRecord::Base has_one :mock_monitoring_service has_one :microsoft_teams_service has_one :packagist_service + has_one :hangouts_chat_service # TODO: replace these relations with the fork network versions has_one :forked_project_link, foreign_key: "forked_to_project_id" @@ -326,6 +327,7 @@ class Project < ActiveRecord::Base scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) } scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) } scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) } + scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) } scope :archived, -> { where(archived: true) } scope :non_archived, -> { where(archived: false) } scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } diff --git a/app/models/project_services/hangouts_chat_service.rb b/app/models/project_services/hangouts_chat_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..a8512c5f57cce7e8d2770cb34ff8ca56a5ea2f2f --- /dev/null +++ b/app/models/project_services/hangouts_chat_service.rb @@ -0,0 +1,67 @@ +require 'hangouts_chat' + +class HangoutsChatService < ChatNotificationService + def title + 'Hangouts Chat' + end + + def description + 'Receive event notifications in Google Hangouts Chat' + end + + def self.to_param + 'hangouts_chat' + end + + def help + 'This service sends notifications about projects events to Google Hangouts Chat room.<br /> + To set up this service: + <ol> + <li><a href="https://developers.google.com/hangouts/chat/how-tos/webhooks">Set up an incoming webhook for your room</a>. All notifications will come to this room.</li> + <li>Paste the <strong>Webhook URL</strong> into the field below.</li> + <li>Select events below to enable notifications.</li> + </ol>' + end + + def event_field(event) + end + + def default_channel_placeholder + end + + def webhook_placeholder + 'https://chat.googleapis.com/v1/spaces…' + end + + def default_fields + [ + { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" }, + { type: 'checkbox', name: 'notify_only_broken_pipelines' }, + { type: 'checkbox', name: 'notify_only_default_branch' } + ] + end + + private + + def notify(message, opts) + simple_text = parse_simple_text_message(message) + HangoutsChat::Sender.new(webhook).simple(simple_text) + end + + def parse_simple_text_message(message) + header = message.pretext + return header if message.attachments.empty? + + attachment = message.attachments.first + title = format_attachment_title(attachment) + body = attachment[:text] + + [header, title, body].compact.join("\n") + end + + def format_attachment_title(attachment) + return attachment[:title] unless attachment[:title_link] + + "<#{attachment[:title_link]}|#{attachment[:title]}>" + end +end diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 3aa56b3983f9c8d23d16c3bb2b73263207596435..f4b3421f04b95b23b949a02ab91283911485fdc6 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -82,7 +82,7 @@ class ProjectWiki # Returns an Array of Gitlab WikiPage instances or an # empty Array if this Wiki has no pages. - def pages(limit: nil) + def pages(limit: 0) wiki.pages(limit: limit).map { |page| WikiPage.new(self, page, true) } end diff --git a/app/models/service.rb b/app/models/service.rb index ad835293b46d4e8b753267b4f52bc80116bc3754..cbfe0c6eedd42b0bc5af54fd5b6037cdef7865f8 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -254,6 +254,7 @@ class Service < ActiveRecord::Base emails_on_push external_wiki flowdock + hangouts_chat hipchat irker jira diff --git a/app/policies/application_setting/term_policy.rb b/app/policies/application_setting/term_policy.rb index f03bf748c762af057296a19e4e1e9795933d541f..17f00f33d35251b3b1743b3d1782364c2ccabcb9 100644 --- a/app/policies/application_setting/term_policy.rb +++ b/app/policies/application_setting/term_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationSetting class TermPolicy < BasePolicy include Gitlab::Utils::StrongMemoize diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb index 603218aa6df312d0b8eac7ce4f74d7d56393d065..0d0f1c28badd0a4e58c32bbb0d2c6d68fd52db3e 100644 --- a/app/policies/base_policy.rb +++ b/app/policies/base_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_dependency 'declarative_policy' class BasePolicy < DeclarativePolicy::Base diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb index 75c7e529902032ddb25b922646c8bed508d8658a..3858b29c82c090bb7c8bf29c5a1cfe27e6c4d356 100644 --- a/app/policies/ci/build_policy.rb +++ b/app/policies/ci/build_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class BuildPolicy < CommitStatusPolicy condition(:protected_ref) do diff --git a/app/policies/ci/pipeline_policy.rb b/app/policies/ci/pipeline_policy.rb index b81329d06256da33646c67948b156eb7634f2c15..f9623587957d711002c528ccc13941f171e02a09 100644 --- a/app/policies/ci/pipeline_policy.rb +++ b/app/policies/ci/pipeline_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class PipelinePolicy < BasePolicy delegate { @subject.project } diff --git a/app/policies/ci/pipeline_schedule_policy.rb b/app/policies/ci/pipeline_schedule_policy.rb index ecba0488d3c44f3c7e787910f01252a14ce044bf..cf3f784f851d20084bc29e2869c8e21ea2d58929 100644 --- a/app/policies/ci/pipeline_schedule_policy.rb +++ b/app/policies/ci/pipeline_schedule_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class PipelineSchedulePolicy < PipelinePolicy alias_method :pipeline_schedule, :subject diff --git a/app/policies/ci/runner_policy.rb b/app/policies/ci/runner_policy.rb index 895abe87d86290ce406f87bdb5cb19540c945a0e..c44f22b6ad3f11f7593547d87041376e088301d6 100644 --- a/app/policies/ci/runner_policy.rb +++ b/app/policies/ci/runner_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class RunnerPolicy < BasePolicy with_options scope: :subject, score: 0 diff --git a/app/policies/ci/trigger_policy.rb b/app/policies/ci/trigger_policy.rb index 5592ac3081218e4fb3bf08fed43440af97e3189a..209db44539c9073092d0a0928e93ef7221dfcd29 100644 --- a/app/policies/ci/trigger_policy.rb +++ b/app/policies/ci/trigger_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class TriggerPolicy < BasePolicy delegate { @subject.project } diff --git a/app/policies/clusters/cluster_policy.rb b/app/policies/clusters/cluster_policy.rb index b5b24491655f9b9f966c317b1111ec16d019e00f..147943a3d6ce361bbc178d79e5c996e5244dc3ca 100644 --- a/app/policies/clusters/cluster_policy.rb +++ b/app/policies/clusters/cluster_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Clusters class ClusterPolicy < BasePolicy alias_method :cluster, :subject diff --git a/app/policies/commit_status_policy.rb b/app/policies/commit_status_policy.rb index 24b2a4cc7fd7bb37cc51f86cc365cf1e975a1a74..eea2a24fb2d2698cc7896ec531c08193e517c17c 100644 --- a/app/policies/commit_status_policy.rb +++ b/app/policies/commit_status_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CommitStatusPolicy < BasePolicy delegate { @subject.project } diff --git a/app/policies/deploy_key_policy.rb b/app/policies/deploy_key_policy.rb index 62a22a59be6e93d32aafd085faf7b46be6aff94f..204c54a5b2036840c7ab916e7387785e59838e86 100644 --- a/app/policies/deploy_key_policy.rb +++ b/app/policies/deploy_key_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployKeyPolicy < BasePolicy with_options scope: :subject, score: 0 condition(:private_deploy_key) { @subject.private? } diff --git a/app/policies/deploy_token_policy.rb b/app/policies/deploy_token_policy.rb index d1b459cfc90d484e4791764b99232e88a8ea082b..e648df3edfc8abc373a04a06797b0fdb443b2ba9 100644 --- a/app/policies/deploy_token_policy.rb +++ b/app/policies/deploy_token_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployTokenPolicy < BasePolicy with_options scope: :subject, score: 0 condition(:maintainer) { @subject.project.team.maintainer?(@user) } diff --git a/app/policies/deployment_policy.rb b/app/policies/deployment_policy.rb index 62b63b9f87bb73dbe485f64e54d155608906fe1b..56ac898b6abdb7801edf7ef94755058840f1d48c 100644 --- a/app/policies/deployment_policy.rb +++ b/app/policies/deployment_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeploymentPolicy < BasePolicy delegate { @subject.project } end diff --git a/app/policies/environment_policy.rb b/app/policies/environment_policy.rb index 2d07311db723630acdd1c5e53b391537f6017140..d1243491f5a3614a9995008ecd4bb20a621c5c34 100644 --- a/app/policies/environment_policy.rb +++ b/app/policies/environment_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class EnvironmentPolicy < BasePolicy delegate { @subject.project } diff --git a/app/policies/external_issue_policy.rb b/app/policies/external_issue_policy.rb index e031b38078cec3dae6303eb89f415c9f98ad2ed3..1106536e075cf4c467a59d69c49816dfe52851ab 100644 --- a/app/policies/external_issue_policy.rb +++ b/app/policies/external_issue_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ExternalIssuePolicy < BasePolicy delegate { @subject.project } end diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb index 1cf5515d9d73517356e9596736a378624bb32abb..6e3827736b2fe58917d13688256b6cae91a33bc5 100644 --- a/app/policies/global_policy.rb +++ b/app/policies/global_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GlobalPolicy < BasePolicy desc "User is blocked" with_options scope: :user, score: 0 diff --git a/app/policies/group_label_policy.rb b/app/policies/group_label_policy.rb index e3dd3296699250fa4391581f2b0ec1759d95ed8e..9f3acd44b2360773d7267ddfa15aa6dfeba42c45 100644 --- a/app/policies/group_label_policy.rb +++ b/app/policies/group_label_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupLabelPolicy < BasePolicy delegate { @subject.group } end diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb index 23dd0d7cd23827d3a0089a24241171df29fccecf..6f1afb87c85f7f8d581ae710a3db4a09b9bcd7ff 100644 --- a/app/policies/group_member_policy.rb +++ b/app/policies/group_member_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupMemberPolicy < BasePolicy delegate :group diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index dc339b71ec7755991a73a42a34a0b0e1552fa020..a8d7a05f50985596f5bb878f87d72f0262561f47 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupPolicy < BasePolicy desc "Group is public" with_options scope: :subject, score: 0 diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb index b431d376e3dd8a2d5c957349d25c0a56bddfc7f2..198bb168d858604006b3ccca209e9e9883f9d41a 100644 --- a/app/policies/issuable_policy.rb +++ b/app/policies/issuable_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssuablePolicy < BasePolicy delegate { @subject.project } diff --git a/app/policies/issue_policy.rb b/app/policies/issue_policy.rb index 263c6e3039c2576b25b70c46368aed6005bbdd8a..94b5f37c682f7b2ae79ab82228545f123553ce9b 100644 --- a/app/policies/issue_policy.rb +++ b/app/policies/issue_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssuePolicy < IssuablePolicy # This class duplicates the same check of Issue#readable_by? for performance reasons # Make sure to sync this class checks with issue.rb to avoid security problems. diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb index c3fe857f8a2dd85fc490e603335f27d25e83dc89..a2950951d0352b5849cdd811fa030e554972e9ff 100644 --- a/app/policies/merge_request_policy.rb +++ b/app/policies/merge_request_policy.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class MergeRequestPolicy < IssuablePolicy end diff --git a/app/policies/namespace_policy.rb b/app/policies/namespace_policy.rb index eb01218eb0aeea0fca83f57cd894d5261b7e1073..2babcb0a2d9440cec9923f46dca99d795f5db740 100644 --- a/app/policies/namespace_policy.rb +++ b/app/policies/namespace_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NamespacePolicy < BasePolicy rule { anonymous }.prevent_all diff --git a/app/policies/nil_policy.rb b/app/policies/nil_policy.rb index 13f46ba60f086a4ba8f7a3d5552a561f44e8560d..fc969f8cd05bbd3baa92b591195329d16de768c6 100644 --- a/app/policies/nil_policy.rb +++ b/app/policies/nil_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NilPolicy < BasePolicy rule { default }.prevent_all end diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb index 077a6761ee6c400858c9280d13948e497babe9b1..bbc2b48b8567e24813d0305c871df7a22d96b545 100644 --- a/app/policies/note_policy.rb +++ b/app/policies/note_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NotePolicy < BasePolicy delegate { @subject.project } delegate { @subject.noteable if DeclarativePolicy.has_policy?(@subject.noteable) } diff --git a/app/policies/personal_snippet_policy.rb b/app/policies/personal_snippet_policy.rb index c1a84727cfa5489fd8714921ecdbd61d04b98e17..777f933cdcd839fb69c3ae03e52ea372bbf29787 100644 --- a/app/policies/personal_snippet_policy.rb +++ b/app/policies/personal_snippet_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PersonalSnippetPolicy < BasePolicy condition(:public_snippet, scope: :subject) { @subject.public? } condition(:is_author) { @user && @subject.author == @user } diff --git a/app/policies/project_label_policy.rb b/app/policies/project_label_policy.rb index 2d0f021118b289188e3f7f63d89bab970c466fd2..5ce896ecaf2e3bb7ad95dbb2aeace6d2b06a3d39 100644 --- a/app/policies/project_label_policy.rb +++ b/app/policies/project_label_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectLabelPolicy < BasePolicy delegate { @subject.project } end diff --git a/app/policies/project_member_policy.rb b/app/policies/project_member_policy.rb index 9aedb620be96a6f0c611e186e3f8e19b9ef6a725..f2f18406bd35b178f368c1526fd87877749a34fc 100644 --- a/app/policies/project_member_policy.rb +++ b/app/policies/project_member_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectMemberPolicy < BasePolicy delegate { @subject.project } diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index bc49092633f163175e35c82e2c6a680064c85dd6..f52a3bad77d3aa95bc015507079283d290d3b6c5 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectPolicy < BasePolicy extend ClassMethods diff --git a/app/policies/project_policy/class_methods.rb b/app/policies/project_policy/class_methods.rb index 60e5aba00baa68df5e7f9e60f798dca58d7f53e5..42d993406a978c27bb0a1da736062f96b222a7c6 100644 --- a/app/policies/project_policy/class_methods.rb +++ b/app/policies/project_policy/class_methods.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectPolicy module ClassMethods def create_read_update_admin_destroy(name) diff --git a/app/policies/project_snippet_policy.rb b/app/policies/project_snippet_policy.rb index dd270643bbf09d83d24d22a711421ba7eababeb4..288bf070cfc7d493ae91f0bcc94108fc4d2c18cd 100644 --- a/app/policies/project_snippet_policy.rb +++ b/app/policies/project_snippet_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectSnippetPolicy < BasePolicy delegate :project diff --git a/app/policies/protected_branch_policy.rb b/app/policies/protected_branch_policy.rb index 1a7faa4db4047607905ecf1a6b2638e93e32d4d6..0e83d2e5834eff01d00358a54b39d01ea6d2b23c 100644 --- a/app/policies/protected_branch_policy.rb +++ b/app/policies/protected_branch_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProtectedBranchPolicy < BasePolicy delegate { @subject.project } diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index ee219f0a0d0f0547839368b7e5a8de5899f6823d..b5717029354c839e0a87ad037c14787ce6ebb469 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserPolicy < BasePolicy desc "The current user is the user in question" condition(:user_is_self, score: 0) { @subject == @user } @@ -5,6 +7,9 @@ class UserPolicy < BasePolicy desc "This is the ghost user" condition(:subject_ghost, scope: :subject, score: 0) { @subject.ghost? } + desc "The profile is private" + condition(:private_profile, scope: :subject, score: 0) { @subject.private_profile? } + rule { ~restricted_public_level }.enable :read_user rule { ~anonymous }.enable :read_user @@ -12,4 +17,7 @@ class UserPolicy < BasePolicy enable :destroy_user enable :update_user end + + rule { default }.enable :read_user_profile + rule { private_profile & ~(user_is_self | admin) }.prevent :read_user_profile end diff --git a/app/presenters/ci/build_metadata_presenter.rb b/app/presenters/ci/build_metadata_presenter.rb index 5048f967ea862cafce4653c82904a64708b7dce2..015b1f67db7217eec267350bfadf25236f2f7f0a 100644 --- a/app/presenters/ci/build_metadata_presenter.rb +++ b/app/presenters/ci/build_metadata_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class BuildMetadataPresenter < Gitlab::View::Presenter::Delegated TIMEOUT_SOURCES = { diff --git a/app/presenters/ci/build_presenter.rb b/app/presenters/ci/build_presenter.rb index e0aaa5cb73630811ec56f895da32c7418f100f17..5331cdf632bc435bbb3603486dee7d8e18f1dd89 100644 --- a/app/presenters/ci/build_presenter.rb +++ b/app/presenters/ci/build_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class BuildPresenter < CommitStatusPresenter def erased_by_user? diff --git a/app/presenters/ci/group_variable_presenter.rb b/app/presenters/ci/group_variable_presenter.rb index 98d68bc7a837c904d74050127472ca3b02d8802a..99011150c84e0f9166e93db89aaf5534623ddf9f 100644 --- a/app/presenters/ci/group_variable_presenter.rb +++ b/app/presenters/ci/group_variable_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class GroupVariablePresenter < Gitlab::View::Presenter::Delegated presents :variable diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb index cc2bce9862d96d75fca58994c193ee2b5e038172..93a38f92073c23e36945fe4aafecc6bffe67cd26 100644 --- a/app/presenters/ci/pipeline_presenter.rb +++ b/app/presenters/ci/pipeline_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class PipelinePresenter < Gitlab::View::Presenter::Delegated include Gitlab::Utils::StrongMemoize diff --git a/app/presenters/ci/variable_presenter.rb b/app/presenters/ci/variable_presenter.rb index 96159f88c5928a014b04b562346f30c34e2dd2d8..f027f3aa5600fa992cd24aa58efe51c9431fcaf3 100644 --- a/app/presenters/ci/variable_presenter.rb +++ b/app/presenters/ci/variable_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Ci class VariablePresenter < Gitlab::View::Presenter::Delegated presents :variable diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb index a424da5ab240e60f4a4089cd520693b67a1e39ee..dfdd8e82f970d71c8de8c2dd153afccb804c20ee 100644 --- a/app/presenters/clusters/cluster_presenter.rb +++ b/app/presenters/clusters/cluster_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Clusters class ClusterPresenter < Gitlab::View::Presenter::Delegated presents :cluster diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb index 9a7aaf4ef32600b60020c35f996179a1aed3f41f..3a9088cfcb82a4d44bbcea13c32b6f2b5f2e9f50 100644 --- a/app/presenters/commit_status_presenter.rb +++ b/app/presenters/commit_status_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CommitStatusPresenter < Gitlab::View::Presenter::Delegated CALLOUT_FAILURE_MESSAGES = { unknown_failure: 'There is an unknown failure, please try again', diff --git a/app/presenters/conversational_development_index/metric_presenter.rb b/app/presenters/conversational_development_index/metric_presenter.rb index bb65ba2646b94a46ffa1e1947cb432735046f77d..e0312c6f43181d277ad3c73889a2411c333f15e3 100644 --- a/app/presenters/conversational_development_index/metric_presenter.rb +++ b/app/presenters/conversational_development_index/metric_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ConversationalDevelopmentIndex class MetricPresenter < Gitlab::View::Presenter::Simple def cards diff --git a/app/presenters/generic_commit_status_presenter.rb b/app/presenters/generic_commit_status_presenter.rb index da09df29a37c8880255470d1cf8dc127b6866f7a..a1dc72d5b98a3963315d7afdd6fe003d6e191509 100644 --- a/app/presenters/generic_commit_status_presenter.rb +++ b/app/presenters/generic_commit_status_presenter.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class GenericCommitStatusPresenter < CommitStatusPresenter end diff --git a/app/presenters/group_member_presenter.rb b/app/presenters/group_member_presenter.rb index 8f53dfa105e81e5e30366826f8af2dd98278a132..c4dcc9e60f9757efb28e991cb9adff8fe1eced06 100644 --- a/app/presenters/group_member_presenter.rb +++ b/app/presenters/group_member_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupMemberPresenter < MemberPresenter private diff --git a/app/presenters/member_presenter.rb b/app/presenters/member_presenter.rb index 7d2f9303b8f4fb4185faad37f57ae68726ff2666..2497bea4aff98b4da9e0f6691154f7c1132e5997 100644 --- a/app/presenters/member_presenter.rb +++ b/app/presenters/member_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MemberPresenter < Gitlab::View::Presenter::Delegated presents :member diff --git a/app/presenters/members_presenter.rb b/app/presenters/members_presenter.rb index e4aba37b69e85607273a26da7c6f6106e31be88b..03ebea36d49b4deb0c7fec606bc68a80a8945067 100644 --- a/app/presenters/members_presenter.rb +++ b/app/presenters/members_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MembersPresenter < Gitlab::View::Presenter::Delegated include Enumerable diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index f77b35416445db293e51d3a56a42155df78c2d1e..ffa238a63d58e04434c68ab0a1a07e86486fb18a 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestPresenter < Gitlab::View::Presenter::Delegated include ActionView::Helpers::UrlHelper include GitlabRoutingHelper diff --git a/app/presenters/project_member_presenter.rb b/app/presenters/project_member_presenter.rb index 7f42d2b70df9b6ceafca6269c02f51fc93cc43b3..e4731074e86183b4b06bf84a6885803242c4e1ff 100644 --- a/app/presenters/project_member_presenter.rb +++ b/app/presenters/project_member_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectMemberPresenter < MemberPresenter private diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb index d4d622d84ab95cc7a82e996a9295658b8f3967e0..4c2f33213d699cf59b6bf7acf3cea83c2946be0c 100644 --- a/app/presenters/project_presenter.rb +++ b/app/presenters/project_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectPresenter < Gitlab::View::Presenter::Delegated include ActionView::Helpers::NumberHelper include ActionView::Helpers::UrlHelper diff --git a/app/presenters/projects/settings/deploy_keys_presenter.rb b/app/presenters/projects/settings/deploy_keys_presenter.rb index c226586fba5ee57ee67dfddbb71e2bcbe7b33765..28eaef00a12981830216dcb1a1beb77bb353e6b7 100644 --- a/app/presenters/projects/settings/deploy_keys_presenter.rb +++ b/app/presenters/projects/settings/deploy_keys_presenter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Projects module Settings class DeployKeysPresenter < Gitlab::View::Presenter::Simple diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb index bdc22d7120261871d3e6426e1e57ff6d63a0ed92..99663c8d5ebdf38a58fc70af0d7c9b8983132226 100644 --- a/app/serializers/analytics_build_entity.rb +++ b/app/serializers/analytics_build_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsBuildEntity < Grape::Entity include RequestAwareEntity include EntityDateHelper diff --git a/app/serializers/analytics_build_serializer.rb b/app/serializers/analytics_build_serializer.rb index f172d67d35627639640271e2d09acc847f3a122d..9c9f76a1c28cd07c6c7c5e04548b9372e0646af7 100644 --- a/app/serializers/analytics_build_serializer.rb +++ b/app/serializers/analytics_build_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsBuildSerializer < BaseSerializer entity AnalyticsBuildEntity end diff --git a/app/serializers/analytics_commit_entity.rb b/app/serializers/analytics_commit_entity.rb index 402cecbfd087cf80bc05f2348b6ba57d26ccc87c..b25c603c9f0bb8706acf044483de56e57046fd76 100644 --- a/app/serializers/analytics_commit_entity.rb +++ b/app/serializers/analytics_commit_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsCommitEntity < CommitEntity include EntityDateHelper diff --git a/app/serializers/analytics_commit_serializer.rb b/app/serializers/analytics_commit_serializer.rb index cdbfecf2b70aab22f6c5dfc68b01423b4a3b794b..0f65687a3c04701f3137d07cc03e41c24919ce1e 100644 --- a/app/serializers/analytics_commit_serializer.rb +++ b/app/serializers/analytics_commit_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsCommitSerializer < BaseSerializer entity AnalyticsCommitEntity end diff --git a/app/serializers/analytics_generic_serializer.rb b/app/serializers/analytics_generic_serializer.rb index 9f4859e841034aae52f1ccc9dad506121f0f6fb0..10391c130347f4a107e94a16840e67b8d9efe048 100644 --- a/app/serializers/analytics_generic_serializer.rb +++ b/app/serializers/analytics_generic_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsGenericSerializer < BaseSerializer def represent(resource, opts = {}) resource.symbolize_keys! diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb index b7d95ea020f4fdf127e0574fca5f641fa47488d8..ab15bd0ac7a92697eccff9f1918f08fd2fa0f971 100644 --- a/app/serializers/analytics_issue_entity.rb +++ b/app/serializers/analytics_issue_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsIssueEntity < Grape::Entity include RequestAwareEntity include EntityDateHelper diff --git a/app/serializers/analytics_issue_serializer.rb b/app/serializers/analytics_issue_serializer.rb index 4fb3e8f1bb4a9bf6126906c898089dde300a4f0d..4a1777276a47c0f0bd55d04b467a485a6ca56f07 100644 --- a/app/serializers/analytics_issue_serializer.rb +++ b/app/serializers/analytics_issue_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsIssueSerializer < AnalyticsGenericSerializer entity AnalyticsIssueEntity end diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb index 888265eaa3853f69e9aad16136363880613acaf3..b7134da94615c28d42a9f9612b3e2609b9b15e59 100644 --- a/app/serializers/analytics_merge_request_entity.rb +++ b/app/serializers/analytics_merge_request_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsMergeRequestEntity < AnalyticsIssueEntity expose :state diff --git a/app/serializers/analytics_merge_request_serializer.rb b/app/serializers/analytics_merge_request_serializer.rb index 4622a1dd8558459ece3da486453b16004d21e48f..f0b9115d02cd04d79cf9b1521c290d01b12a5276 100644 --- a/app/serializers/analytics_merge_request_serializer.rb +++ b/app/serializers/analytics_merge_request_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsMergeRequestSerializer < AnalyticsGenericSerializer entity AnalyticsMergeRequestEntity end diff --git a/app/serializers/analytics_stage_entity.rb b/app/serializers/analytics_stage_entity.rb index 3e355a13e066d4b90ac8d3e375572c0d8d3dac3e..ae7c20c3bbaad3b00b40cca632b224ce1a3aa763 100644 --- a/app/serializers/analytics_stage_entity.rb +++ b/app/serializers/analytics_stage_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsStageEntity < Grape::Entity include EntityDateHelper diff --git a/app/serializers/analytics_stage_serializer.rb b/app/serializers/analytics_stage_serializer.rb index 613cf6874d866750d4497ae0630797008779eece..86786273240396a84e7cf077d9f3fa924e2ed0b2 100644 --- a/app/serializers/analytics_stage_serializer.rb +++ b/app/serializers/analytics_stage_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsStageSerializer < BaseSerializer entity AnalyticsStageEntity end diff --git a/app/serializers/analytics_summary_entity.rb b/app/serializers/analytics_summary_entity.rb index 9c37afd53e1ac5972757443b531f5f33047848ec..39c6b4b06b22d4d05278ad939eee8aa08be31126 100644 --- a/app/serializers/analytics_summary_entity.rb +++ b/app/serializers/analytics_summary_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsSummaryEntity < Grape::Entity expose :value, safe: true expose :title diff --git a/app/serializers/analytics_summary_serializer.rb b/app/serializers/analytics_summary_serializer.rb index c87a24aa47c0df78f7bd40709cd151497abfc47e..b22bd737f03a5900d7742a9ad89c7789c528317e 100644 --- a/app/serializers/analytics_summary_serializer.rb +++ b/app/serializers/analytics_summary_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsSummarySerializer < BaseSerializer entity AnalyticsSummaryEntity end diff --git a/app/serializers/award_emoji_entity.rb b/app/serializers/award_emoji_entity.rb index 6e03cd02392b2b34ddab529b3664bd0afb9f5461..212931a2fa9611d15cbf44221a72b0d01001c502 100644 --- a/app/serializers/award_emoji_entity.rb +++ b/app/serializers/award_emoji_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AwardEmojiEntity < Grape::Entity expose :name expose :user, using: API::Entities::UserSafe diff --git a/app/serializers/base_serializer.rb b/app/serializers/base_serializer.rb index 8cade280b0c633d712723661bf6e6f7711208ca3..7b65bd22f54a95c7811b6fc6de483815c1aa5cc0 100644 --- a/app/serializers/base_serializer.rb +++ b/app/serializers/base_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BaseSerializer attr_reader :params diff --git a/app/serializers/blob_entity.rb b/app/serializers/blob_entity.rb index b501fd5e964845ff793f7f8daf112dcda4cff2c7..3ac61481deab3b50c1d6e95a1250c124b8276c49 100644 --- a/app/serializers/blob_entity.rb +++ b/app/serializers/blob_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BlobEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/build_action_entity.rb b/app/serializers/build_action_entity.rb index f2d76a8ad813b1f0d3854e74b407a3236f62f299..f9da3f639115adde848ffbaa685cac76015d3754 100644 --- a/app/serializers/build_action_entity.rb +++ b/app/serializers/build_action_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildActionEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/build_artifact_entity.rb b/app/serializers/build_artifact_entity.rb index 6e0e33bc09b758d48b5ea437dd405d9c5caebe53..414f436e76ecc74efe5990ede500f5282352bd83 100644 --- a/app/serializers/build_artifact_entity.rb +++ b/app/serializers/build_artifact_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildArtifactEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb index 2de9624aed407573ff5254fa409e9d67739f4b1d..b887b99d31c3bda5f4739eae65f3b6df54115e1c 100644 --- a/app/serializers/build_details_entity.rb +++ b/app/serializers/build_details_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildDetailsEntity < JobEntity expose :coverage, :erased_at, :duration expose :tag_list, as: :tags diff --git a/app/serializers/build_metadata_entity.rb b/app/serializers/build_metadata_entity.rb index f16f3badffa0a8cfaec0c3417c6f10e15fcfd4cb..6242ee8957d5c7b121b0595c8c9cea981f4d9b25 100644 --- a/app/serializers/build_metadata_entity.rb +++ b/app/serializers/build_metadata_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildMetadataEntity < Grape::Entity expose :timeout_human_readable expose :timeout_source do |metadata| diff --git a/app/serializers/build_serializer.rb b/app/serializers/build_serializer.rb index bae9932847f272e701872a3882ff36f8f4966367..0649fdad6a89afdc6faa303f85ed9e303bc9916e 100644 --- a/app/serializers/build_serializer.rb +++ b/app/serializers/build_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildSerializer < BaseSerializer entity JobEntity diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb index 77fc3336521af9dfa1c6aec37b674f9f1bfa0732..2bd17e58086d66cce20b5c919c2c7a2e3017ec8b 100644 --- a/app/serializers/cluster_application_entity.rb +++ b/app/serializers/cluster_application_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ClusterApplicationEntity < Grape::Entity expose :name expose :status_name, as: :status diff --git a/app/serializers/cluster_entity.rb b/app/serializers/cluster_entity.rb index 7e5b0997878368d8a579eba978d84f56547bff1e..c59f68bbc4966298a39929600bd3fef7370f21bb 100644 --- a/app/serializers/cluster_entity.rb +++ b/app/serializers/cluster_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ClusterEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/cluster_serializer.rb b/app/serializers/cluster_serializer.rb index 2e13c1501e78988d707a1215dd6296c20fe35ac6..4bb4d4880d4363c2cdd3204abdbd2a3e592d8025 100644 --- a/app/serializers/cluster_serializer.rb +++ b/app/serializers/cluster_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ClusterSerializer < BaseSerializer entity ClusterEntity diff --git a/app/serializers/cohort_activity_month_entity.rb b/app/serializers/cohort_activity_month_entity.rb index e6788a8b5966869c4b1da64cc66adc0eaf014613..cdbc89a2dcd14d2d2328c75e8f2a14bc7c88b213 100644 --- a/app/serializers/cohort_activity_month_entity.rb +++ b/app/serializers/cohort_activity_month_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CohortActivityMonthEntity < Grape::Entity include ActionView::Helpers::NumberHelper diff --git a/app/serializers/cohort_entity.rb b/app/serializers/cohort_entity.rb index 7cdba5b04846039a6567ca0397f7612ed0eed7ff..3d0213e1038f096d1edce8405836a64fec83ab22 100644 --- a/app/serializers/cohort_entity.rb +++ b/app/serializers/cohort_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CohortEntity < Grape::Entity include ActionView::Helpers::NumberHelper diff --git a/app/serializers/cohorts_entity.rb b/app/serializers/cohorts_entity.rb index 98f5995ba6f760178b1790b8e326c16987d6253d..a84d568bf30f82d1b395d100db8f9bfc6c196e45 100644 --- a/app/serializers/cohorts_entity.rb +++ b/app/serializers/cohorts_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CohortsEntity < Grape::Entity expose :months_included expose :cohorts, using: CohortEntity diff --git a/app/serializers/cohorts_serializer.rb b/app/serializers/cohorts_serializer.rb index fe9367b13d895951dcd0041b12d0a34050763e11..ceca5e1e5a8dabaf65561a529fa4118ad20615c4 100644 --- a/app/serializers/cohorts_serializer.rb +++ b/app/serializers/cohorts_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CohortsSerializer < AnalyticsGenericSerializer entity CohortsEntity end diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb index c8dd98cc04d96177b15928829638fb5070981149..b3287c66554cca42bdc471a7230fafd934dbe936 100644 --- a/app/serializers/commit_entity.rb +++ b/app/serializers/commit_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CommitEntity < API::Entities::Commit include RequestAwareEntity diff --git a/app/serializers/concerns/with_pagination.rb b/app/serializers/concerns/with_pagination.rb index 89631b73fcfff16d9e2afaca16bf5c8801e87a10..c8ffae355e85376d7ef973474215a62e4fea9061 100644 --- a/app/serializers/concerns/with_pagination.rb +++ b/app/serializers/concerns/with_pagination.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module WithPagination attr_accessor :paginator diff --git a/app/serializers/container_repositories_serializer.rb b/app/serializers/container_repositories_serializer.rb index 56dc70b568702adf8ef1c7b2c9328f9f58eab96c..e1ce3c7b3ae0dea76ccee4b925c7a4e20dc4e836 100644 --- a/app/serializers/container_repositories_serializer.rb +++ b/app/serializers/container_repositories_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ContainerRepositoriesSerializer < BaseSerializer entity ContainerRepositoryEntity end diff --git a/app/serializers/container_repository_entity.rb b/app/serializers/container_repository_entity.rb index 1103cf30a07d5990ba09296a9e1c9bbed083b117..59bf35f5affc095e8296e82a092ccf897d24d4ac 100644 --- a/app/serializers/container_repository_entity.rb +++ b/app/serializers/container_repository_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ContainerRepositoryEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/container_tag_entity.rb b/app/serializers/container_tag_entity.rb index 8f1488e6cbb8f0da429d5cebda236b87ce346f5d..637294877f82f5dd97a7f17ab08377c87f3abc88 100644 --- a/app/serializers/container_tag_entity.rb +++ b/app/serializers/container_tag_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ContainerTagEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/container_tags_serializer.rb b/app/serializers/container_tags_serializer.rb index 6ff3adff135e642f623f7386bed5d5e91cc151ca..982ce33f6e33a362e2d3e30abe404a28057b20ae 100644 --- a/app/serializers/container_tags_serializer.rb +++ b/app/serializers/container_tags_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ContainerTagsSerializer < BaseSerializer entity ContainerTagEntity diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb index 2678f99510c0e80edc4c091488e794b204925fbe..54bf030aba1d2a50d44b62be6f201adf80c4daed 100644 --- a/app/serializers/deploy_key_entity.rb +++ b/app/serializers/deploy_key_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployKeyEntity < Grape::Entity expose :id expose :user_id diff --git a/app/serializers/deploy_key_serializer.rb b/app/serializers/deploy_key_serializer.rb index 8f849eb88b717e132fb57b98d52d5ec4acb31253..a1cd98b631b4c6b2dcf4c09ccc6571dc9ea13884 100644 --- a/app/serializers/deploy_key_serializer.rb +++ b/app/serializers/deploy_key_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployKeySerializer < BaseSerializer entity DeployKeyEntity end diff --git a/app/serializers/deploy_keys_project_entity.rb b/app/serializers/deploy_keys_project_entity.rb index 568ef5ab75e3859cffebcd1216dbde6e8474226d..5775ad72d0d57717ea97f94a1679b5f5c37c3f5c 100644 --- a/app/serializers/deploy_keys_project_entity.rb +++ b/app/serializers/deploy_keys_project_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployKeysProjectEntity < Grape::Entity expose :can_push expose :project, using: ProjectEntity diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb index 241c689bccdd6822630b198c0a1101946da9d9ca..344148a1fb76754da38e48bc6c3fc3b2cb4b0bfc 100644 --- a/app/serializers/deployment_entity.rb +++ b/app/serializers/deployment_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeploymentEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/deployment_serializer.rb b/app/serializers/deployment_serializer.rb index cba5c3f311f77f9ad3e5a84bedb7381639b86fcc..04db6b884896ecfa33dc22c07425fb148c4e8440 100644 --- a/app/serializers/deployment_serializer.rb +++ b/app/serializers/deployment_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeploymentSerializer < BaseSerializer entity DeploymentEntity diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb index 61135fba97bc07fc1e819a29685746274e4fda12..79844c9210a2f6fcd3cf67acc8af1be9862e8118 100644 --- a/app/serializers/diff_file_entity.rb +++ b/app/serializers/diff_file_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiffFileEntity < Grape::Entity include RequestAwareEntity include BlobHelper diff --git a/app/serializers/diffs_entity.rb b/app/serializers/diffs_entity.rb index bb804e5347a166e1f6d242fab35a7ae5121230ec..f75ace14d9cad08e1ea73aa889c299c62eb0fa95 100644 --- a/app/serializers/diffs_entity.rb +++ b/app/serializers/diffs_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiffsEntity < Grape::Entity include DiffHelper include RequestAwareEntity diff --git a/app/serializers/diffs_serializer.rb b/app/serializers/diffs_serializer.rb index 6771e10c5acdfe10d3c2db367ec45d71bde1bd26..9e5eb1699d41be06ac52b871b60537e1694fc9cc 100644 --- a/app/serializers/diffs_serializer.rb +++ b/app/serializers/diffs_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiffsSerializer < BaseSerializer entity DiffsEntity end diff --git a/app/serializers/discussion_entity.rb b/app/serializers/discussion_entity.rb index 7505bbdeb3d4335220cc956777402916683e490c..6f95e6f9ca1582c56999da1dc2072c4d3b53273d 100644 --- a/app/serializers/discussion_entity.rb +++ b/app/serializers/discussion_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiscussionEntity < Grape::Entity include RequestAwareEntity include NotesHelper diff --git a/app/serializers/discussion_serializer.rb b/app/serializers/discussion_serializer.rb index ed5e1224bb23b58c5f63cf0f396d131e2a8029e7..5be40e74175f38c7da8eea3bb4289cb1695ac92e 100644 --- a/app/serializers/discussion_serializer.rb +++ b/app/serializers/discussion_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiscussionSerializer < BaseSerializer entity DiscussionEntity end diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb index 464217123b4f10af415955f2c64c14eb1e405bc6..cc0c2abf8632c1e3290e7a56f0ea0a40b7e24514 100644 --- a/app/serializers/entity_date_helper.rb +++ b/app/serializers/entity_date_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module EntityDateHelper include ActionView::Helpers::DateHelper include ActionView::Helpers::TagHelper @@ -50,15 +52,20 @@ module EntityDateHelper elsif entity.due_date is_upcoming = (entity.due_date - Date.today).to_i > 0 time_ago = time_ago_in_words(entity.due_date) - content = time_ago.gsub(/\d+/) { |match| "<strong>#{match}</strong>" } - content.slice!("about ") - content << " " + (is_upcoming ? _("remaining") : _("ago")) - content.html_safe + + # https://gitlab.com/gitlab-org/gitlab-ce/issues/49440 + # + # Need to improve the i18n here and do a full translation + # of the string instead of piecewise translations. + content = time_ago + .gsub(/\d+/) { |match| "<strong>#{match}</strong>" } + .remove("about ") + remaining_or_ago = is_upcoming ? _("remaining") : _("ago") + + "#{content} #{remaining_or_ago}".html_safe elsif entity.start_date && entity.start_date.past? - days = entity.elapsed_days - content = content_tag(:strong, days) - content << " #{'day'.pluralize(days)} elapsed" - content.html_safe + days = entity.elapsed_days + "#{content_tag(:strong, days)} #{'day'.pluralize(days)} elapsed".html_safe end end end diff --git a/app/serializers/entity_request.rb b/app/serializers/entity_request.rb index 456ba1174c0634486d37a9bb036a252b17c2e674..49e026e8c2ae471e14fc1063d729b2114c49e4a8 100644 --- a/app/serializers/entity_request.rb +++ b/app/serializers/entity_request.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class EntityRequest # We use EntityRequest object to collect parameters and variables # from the controller. Because options that are being passed to the entity diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb index 83558fc66599011da42e7c0452bec91ad33e0cc3..b18e9706db6e787f469aa212a7a78ebbfabfe9de 100644 --- a/app/serializers/environment_entity.rb +++ b/app/serializers/environment_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class EnvironmentEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb index 84722f33f59d6c400735ca356dece4e3fce3e42d..dc1686c30c4a4be23ca62397d309c60d145438b1 100644 --- a/app/serializers/environment_serializer.rb +++ b/app/serializers/environment_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class EnvironmentSerializer < BaseSerializer include WithPagination diff --git a/app/serializers/group_child_entity.rb b/app/serializers/group_child_entity.rb index ee150eefd9ec2bc3824c3b4aa5a6bf3326630608..f6804fe7f6a728e47d30f14c0c89a6413c2f781d 100644 --- a/app/serializers/group_child_entity.rb +++ b/app/serializers/group_child_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupChildEntity < Grape::Entity include ActionView::Helpers::NumberHelper include RequestAwareEntity diff --git a/app/serializers/group_child_serializer.rb b/app/serializers/group_child_serializer.rb index 2baef0a57034a689d1f7f57d37e2674372f82bd3..789707c2c9bbac81c54a4c64a5cab1cefe8ff7e0 100644 --- a/app/serializers/group_child_serializer.rb +++ b/app/serializers/group_child_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupChildSerializer < BaseSerializer include WithPagination diff --git a/app/serializers/group_entity.rb b/app/serializers/group_entity.rb index 6d8466da902d6c6c834693b80fa4cb7502830060..c46c342ee5ddf3817620ff99a3090ec8a42aedad 100644 --- a/app/serializers/group_entity.rb +++ b/app/serializers/group_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupEntity < Grape::Entity include ActionView::Helpers::NumberHelper include RequestAwareEntity diff --git a/app/serializers/group_serializer.rb b/app/serializers/group_serializer.rb index 8cf7eb63bcf0123194b16a00b6d5f4e87fbe075c..38c5757a6c1ce6d6725c5747e311abf9f2ac7e1f 100644 --- a/app/serializers/group_serializer.rb +++ b/app/serializers/group_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupSerializer < BaseSerializer include WithPagination diff --git a/app/serializers/group_variable_entity.rb b/app/serializers/group_variable_entity.rb index 62cf0b21e1e3df90443fa4f7a81638313468e3b7..0edab4a309257b6b6f30ab22340aff348ec05cb8 100644 --- a/app/serializers/group_variable_entity.rb +++ b/app/serializers/group_variable_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupVariableEntity < Grape::Entity expose :id expose :key diff --git a/app/serializers/group_variable_serializer.rb b/app/serializers/group_variable_serializer.rb index 8f8205924aadaaa20cd343c7f233c757da010a04..ed20b240cce5d90fbaab822f95947d352b78272f 100644 --- a/app/serializers/group_variable_serializer.rb +++ b/app/serializers/group_variable_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupVariableSerializer < BaseSerializer entity GroupVariableEntity end diff --git a/app/serializers/issuable_entity.rb b/app/serializers/issuable_entity.rb index 6f31fbd6b7c193973d20c268d0620e0636356dfb..e71bd3313fbb1bb52d50d52faeb1434cf3d18739 100644 --- a/app/serializers/issuable_entity.rb +++ b/app/serializers/issuable_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssuableEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/issuable_sidebar_entity.rb b/app/serializers/issuable_sidebar_entity.rb index 29138c803dfdd986864fb809dc571857f1565588..773d78d324cbcf2d7b60997b4a6c67f204630583 100644 --- a/app/serializers/issuable_sidebar_entity.rb +++ b/app/serializers/issuable_sidebar_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssuableSidebarEntity < Grape::Entity include TimeTrackableEntity include RequestAwareEntity diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index 840fdbcbf1409446bc567aead151abf9c6077fe8..16a477c92fa08760190425ef3063ccd216311500 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssueEntity < IssuableEntity include TimeTrackableEntity diff --git a/app/serializers/issue_serializer.rb b/app/serializers/issue_serializer.rb index 2555595379b553b76d53ad03dddbbaf215809b82..37cf5e28396a8039aec1833180df12de18d9d2fe 100644 --- a/app/serializers/issue_serializer.rb +++ b/app/serializers/issue_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssueSerializer < BaseSerializer # This overrided method takes care of which entity should be used # to serialize the `issue` based on `basic` key in `opts` param. diff --git a/app/serializers/issue_sidebar_entity.rb b/app/serializers/issue_sidebar_entity.rb index 6c823dbfe951b2b5f80dc2671b5378bde7d72d8d..349ad9d1fef42f157b4f13b61c75c4178671e963 100644 --- a/app/serializers/issue_sidebar_entity.rb +++ b/app/serializers/issue_sidebar_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssueSidebarEntity < IssuableSidebarEntity expose :assignees, using: API::Entities::UserBasic end diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb index 960e7291ae62e5ffbf0ca2e81b423430b2074539..7bc1d87dea5a7a3646f87e750bdf3aaf58543b34 100644 --- a/app/serializers/job_entity.rb +++ b/app/serializers/job_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class JobEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/job_group_entity.rb b/app/serializers/job_group_entity.rb index 8554de555177e8f3458a4da38d286457f962d89a..0941a9d36be9e43a6147435b1a90bb1f7ec5431e 100644 --- a/app/serializers/job_group_entity.rb +++ b/app/serializers/job_group_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class JobGroupEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/label_entity.rb b/app/serializers/label_entity.rb index 4452161051e4c44955c8f13f76e71c1861d8f03b..98743d62b501fa81393e70db5fbe1b7177bb0a2d 100644 --- a/app/serializers/label_entity.rb +++ b/app/serializers/label_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LabelEntity < Grape::Entity expose :id, if: ->(label, _) { !label.is_a?(GlobalLabel) } diff --git a/app/serializers/label_serializer.rb b/app/serializers/label_serializer.rb index ad6ba8c46c99e3256a6274d612051eb515a30c63..25b9f7de243d104f476c39871f2c6fc8cae07f25 100644 --- a/app/serializers/label_serializer.rb +++ b/app/serializers/label_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LabelSerializer < BaseSerializer entity LabelEntity diff --git a/app/serializers/lfs_file_lock_entity.rb b/app/serializers/lfs_file_lock_entity.rb index 264a77adc3fb13599e9aab9ff8d55c60db77a797..7961c4e666b374492f8c13ae5d14aca2d7d0f760 100644 --- a/app/serializers/lfs_file_lock_entity.rb +++ b/app/serializers/lfs_file_lock_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LfsFileLockEntity < Grape::Entity root 'locks', 'lock' diff --git a/app/serializers/lfs_file_lock_serializer.rb b/app/serializers/lfs_file_lock_serializer.rb index ba8fb1a461d068b74f957f3c9f711ff0a6537084..0367097e3764399746330b911a2c95eef67bff98 100644 --- a/app/serializers/lfs_file_lock_serializer.rb +++ b/app/serializers/lfs_file_lock_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LfsFileLockSerializer < BaseSerializer entity LfsFileLockEntity end diff --git a/app/serializers/merge_request_basic_entity.rb b/app/serializers/merge_request_basic_entity.rb index 1c06691026df28d4a61590bd9591ba3a47842d9e..f7eb74cf3923fd0b96298dbeb01c491648acd78f 100644 --- a/app/serializers/merge_request_basic_entity.rb +++ b/app/serializers/merge_request_basic_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestBasicEntity < IssuableSidebarEntity expose :assignee_id expose :merge_status diff --git a/app/serializers/merge_request_basic_serializer.rb b/app/serializers/merge_request_basic_serializer.rb index cc5c664c8fa99a467726dee154e616ce318ab039..a68b48b00db3d4e29b5fdbd9972e2b081753d4f9 100644 --- a/app/serializers/merge_request_basic_serializer.rb +++ b/app/serializers/merge_request_basic_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestBasicSerializer < BaseSerializer entity MergeRequestBasicEntity end diff --git a/app/serializers/merge_request_create_entity.rb b/app/serializers/merge_request_create_entity.rb index 11234313293cd4608a71204c068c03e7589ad0fd..e7a93004dda6d735a0151ead699bb8e50c379a7f 100644 --- a/app/serializers/merge_request_create_entity.rb +++ b/app/serializers/merge_request_create_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestCreateEntity < Grape::Entity expose :iid diff --git a/app/serializers/merge_request_create_serializer.rb b/app/serializers/merge_request_create_serializer.rb index 08daf473319fd278a1f3c7b03a16271238c08d2b..b6df9ee13fc9d0d143b918003282f02eb4e9766e 100644 --- a/app/serializers/merge_request_create_serializer.rb +++ b/app/serializers/merge_request_create_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestCreateSerializer < BaseSerializer entity MergeRequestCreateEntity end diff --git a/app/serializers/merge_request_diff_entity.rb b/app/serializers/merge_request_diff_entity.rb index 32c761b45aceb375950eba7e24804857a44ee138..433bfe6047450cdd55b7050563ef06b0f51903cd 100644 --- a/app/serializers/merge_request_diff_entity.rb +++ b/app/serializers/merge_request_diff_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestDiffEntity < Grape::Entity include Gitlab::Routing include GitHelper diff --git a/app/serializers/merge_request_metrics_entity.rb b/app/serializers/merge_request_metrics_entity.rb index 3548107ac168d12e91d45bf3c17283378b33117e..1c9db08d1039346bd2354f81d24be06036541649 100644 --- a/app/serializers/merge_request_metrics_entity.rb +++ b/app/serializers/merge_request_metrics_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestMetricsEntity < Grape::Entity expose :latest_closed_at, as: :closed_at expose :merged_at diff --git a/app/serializers/merge_request_serializer.rb b/app/serializers/merge_request_serializer.rb index caf193bdae37556bad0e51dec256568f085e8302..1f8c830e1aa16bbf04565b740f11900b2cb051b5 100644 --- a/app/serializers/merge_request_serializer.rb +++ b/app/serializers/merge_request_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestSerializer < BaseSerializer # This overrided method takes care of which entity should be used # to serialize the `merge_request` based on `serializer` key in `opts` param. diff --git a/app/serializers/merge_request_user_entity.rb b/app/serializers/merge_request_user_entity.rb index 33fc7b724d5d7a7652695e5cc5504af04b44ac11..fd2d289711346fce62f54103c38a23328f8aa00f 100644 --- a/app/serializers/merge_request_user_entity.rb +++ b/app/serializers/merge_request_user_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestUserEntity < UserEntity include RequestAwareEntity include BlobHelper diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index a78bd77cf7c8abd828a95088dff6ffe981a10c1f..4fe04e4b206e99f9414a79322b58e29a7ec67339 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestWidgetEntity < IssuableEntity expose :state expose :in_progress_merge_commit_sha diff --git a/app/serializers/note_attachment_entity.rb b/app/serializers/note_attachment_entity.rb index 1ad50568ab96c2369c779cea0a7c5119cf54bd9e..dc801e2bf4e936eb1e92efe17e75c44a1d839bd1 100644 --- a/app/serializers/note_attachment_entity.rb +++ b/app/serializers/note_attachment_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NoteAttachmentEntity < Grape::Entity expose :url expose :filename diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb index 0e1f94a9f61ab0bb25527d178426cb9642639fb3..daa5c24d0f5126a98e7e9a833e945b3d599efe2b 100644 --- a/app/serializers/note_entity.rb +++ b/app/serializers/note_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NoteEntity < API::Entities::Note include RequestAwareEntity include NotesHelper diff --git a/app/serializers/note_user_entity.rb b/app/serializers/note_user_entity.rb index 7289f3a022257fb8e420e8e51742cdc04fc87de9..b00dfa7d353ccebf349a19b26b6fc93c3d811768 100644 --- a/app/serializers/note_user_entity.rb +++ b/app/serializers/note_user_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NoteUserEntity < UserEntity unexpose :web_url end diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb index 8ba9cac53c4c5894f3caa701948bbdaa7c852bfe..3b56767f774f797d8411c6fd1e2e298c731d97a4 100644 --- a/app/serializers/pipeline_details_entity.rb +++ b/app/serializers/pipeline_details_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PipelineDetailsEntity < PipelineEntity expose :details do expose :ordered_stages, as: :stages, using: StageEntity diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb index f782b411b8405b6efeeb0632e7c77b1793614378..6cf1925adda6c17f30bcc8c251409b3f4f0ef5a3 100644 --- a/app/serializers/pipeline_entity.rb +++ b/app/serializers/pipeline_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PipelineEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb index 17a022539bb39b9afc787730268c80e5919fca0e..4a33160afa1a3a033dbf66400624d8b0a26c8180 100644 --- a/app/serializers/pipeline_serializer.rb +++ b/app/serializers/pipeline_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PipelineSerializer < BaseSerializer include WithPagination entity PipelineDetailsEntity diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb index b3e5fd21e971c3e6eeadc3c791c21e9a9aa10e85..60c4ba135d6b4b4c580d8526d0c1c463be4269e0 100644 --- a/app/serializers/project_entity.rb +++ b/app/serializers/project_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/project_mirror_entity.rb b/app/serializers/project_mirror_entity.rb index a9c08ac021ad3b4940373790937b22023560db0a..8aba244cd11bb659681a573bae9c0ed900e7ff49 100644 --- a/app/serializers/project_mirror_entity.rb +++ b/app/serializers/project_mirror_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectMirrorEntity < Grape::Entity expose :id diff --git a/app/serializers/project_note_entity.rb b/app/serializers/project_note_entity.rb index e541bfbee8d11bc6fb4c5af678ad79c06d3702d6..d7c4d0aacc68075c97acd714e5639eba7977b0c4 100644 --- a/app/serializers/project_note_entity.rb +++ b/app/serializers/project_note_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectNoteEntity < NoteEntity expose :human_access do |note| note.project.team.human_max_access(note.author_id) diff --git a/app/serializers/project_note_serializer.rb b/app/serializers/project_note_serializer.rb index 763ad0bdb3fab85f167044f907e2b9b267d8895e..2182904e81521c71d5271fff8f126929d5248e37 100644 --- a/app/serializers/project_note_serializer.rb +++ b/app/serializers/project_note_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectNoteSerializer < BaseSerializer entity ProjectNoteEntity end diff --git a/app/serializers/project_serializer.rb b/app/serializers/project_serializer.rb index 74de1e79a8f832f6bc9d2373ea04c823b307b00e..23b96c2fc9e2397d6a77f377a56b8b96bee197be 100644 --- a/app/serializers/project_serializer.rb +++ b/app/serializers/project_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectSerializer < BaseSerializer entity ProjectEntity end diff --git a/app/serializers/request_aware_entity.rb b/app/serializers/request_aware_entity.rb index d53fcfb8c1b3d0b2e832ed7f62c8e5bede07ca81..1524c1291d83fe9008d8f70477057e9baf00b1be 100644 --- a/app/serializers/request_aware_entity.rb +++ b/app/serializers/request_aware_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module RequestAwareEntity extend ActiveSupport::Concern diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb index db26eadab2de02843c7e575c18715d9613992e47..04ec80e0efacafe79a45281a331945b1b42042d4 100644 --- a/app/serializers/runner_entity.rb +++ b/app/serializers/runner_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class RunnerEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb index 2516df70ad916c84a5ca6194260eb5971907b102..00e6d32ee3a9b21dd61e9e3ce04326e2aa7c2daa 100644 --- a/app/serializers/stage_entity.rb +++ b/app/serializers/stage_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StageEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/stage_serializer.rb b/app/serializers/stage_serializer.rb index 091d8e91e433dabe6ec18491e987ba4c1224593e..11fb0d3f852ba0dd9ccd171021c6e5476fb83f34 100644 --- a/app/serializers/stage_serializer.rb +++ b/app/serializers/stage_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StageSerializer < BaseSerializer include WithPagination diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb index 47df7f9dcf9012a3fae5e29145c0a5d9abba6856..306c30f0323b8529d6603274cfa095b440271989 100644 --- a/app/serializers/status_entity.rb +++ b/app/serializers/status_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StatusEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/submodule_entity.rb b/app/serializers/submodule_entity.rb index ed1f1ae0ef07e35075728f8b060db5be2d1a1817..e475a4f301faf67196025b2066cabe87e187a9da 100644 --- a/app/serializers/submodule_entity.rb +++ b/app/serializers/submodule_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SubmoduleEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/time_trackable_entity.rb b/app/serializers/time_trackable_entity.rb index e81cd7bec72aecf9938996c68308eeb0145abdc0..613d19703a4f0365e343a534da8a225966993a38 100644 --- a/app/serializers/time_trackable_entity.rb +++ b/app/serializers/time_trackable_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module TimeTrackableEntity extend ActiveSupport::Concern extend Grape diff --git a/app/serializers/tree_entity.rb b/app/serializers/tree_entity.rb index 9f1b485347fbfccf35d3c53b19f875a0bac3b42e..9b7dc80e1d9dd5e92eb65b4233929b8ea52fed6e 100644 --- a/app/serializers/tree_entity.rb +++ b/app/serializers/tree_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TreeEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/tree_root_entity.rb b/app/serializers/tree_root_entity.rb index 496f070ddbd658f1f05e25e5cc7494535891643a..f1cfcd943d8a8699a59231727fa79a00990972e7 100644 --- a/app/serializers/tree_root_entity.rb +++ b/app/serializers/tree_root_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # TODO: Inherit from TreeEntity, when `Tree` implements `id` and `name` like `Gitlab::Git::Tree`. class TreeRootEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/tree_serializer.rb b/app/serializers/tree_serializer.rb index 713ade23bc9c38c68ce6abd82e7f8d18c765b05e..536b8ab1ae2adec33aabfbc4f665891b8e7e7471 100644 --- a/app/serializers/tree_serializer.rb +++ b/app/serializers/tree_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TreeSerializer < BaseSerializer entity TreeRootEntity end diff --git a/app/serializers/user_entity.rb b/app/serializers/user_entity.rb index 876512b12dc6c16db081f22e86039f29c1b8b46d..6236d66ff4a18f5703fde952924361d382b36167 100644 --- a/app/serializers/user_entity.rb +++ b/app/serializers/user_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserEntity < API::Entities::UserBasic include RequestAwareEntity diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 49a71ebac614eb7c34caf612bdbd5905d667e137..2111e1b5667fd801d0d46f653fd66ad1ae25c647 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserSerializer < BaseSerializer entity UserEntity end diff --git a/app/serializers/variable_entity.rb b/app/serializers/variable_entity.rb index d576745c07320129187f812506f8cb79275e8047..85cf367fe510399c5fceb220a96d977e2e8864f0 100644 --- a/app/serializers/variable_entity.rb +++ b/app/serializers/variable_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class VariableEntity < Grape::Entity expose :id expose :key diff --git a/app/serializers/variable_serializer.rb b/app/serializers/variable_serializer.rb index 32ae82ab51c8d981911fb3b39923ddc553b4def8..586666cad8e24e5b0abebd0937d02e585f8f5a0e 100644 --- a/app/serializers/variable_serializer.rb +++ b/app/serializers/variable_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class VariableSerializer < BaseSerializer entity VariableEntity end diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index e560d40371ef0a42133abc5f3406a1a534153167..0c426faa22de986506f0036111f8483a4daa6f46 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -25,7 +25,7 @@ module Projects success rescue => e - error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}") + error("Error importing repository #{project.safe_import_url} into #{project.full_path} - #{e.message}") end private diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb index c69b46cab5a58673ed0d2e6f42a1ddd2c641173d..acc2fa153ae8336ac17b9d6e6676a041b704bae0 100644 --- a/app/services/users/build_service.rb +++ b/app/services/users/build_service.rb @@ -64,7 +64,8 @@ module Users :theme_id, :twitter, :username, - :website_url + :website_url, + :private_profile ] end diff --git a/app/uploaders/import_export_uploader.rb b/app/uploaders/import_export_uploader.rb index 213ac5c801197e49a276ff9f4c97657409f4b8a5..7c45ba5ca95f0921e637946ac2a6803e4616894a 100644 --- a/app/uploaders/import_export_uploader.rb +++ b/app/uploaders/import_export_uploader.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ImportExportUploader < AttachmentUploader EXTENSION_WHITELIST = %w[tar.gz].freeze diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 18f2c1a509f78bdbb711a388f03d9cc721fb3079..fac61f9d2494567a505d06b12cd71190e90f566b 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -91,10 +91,10 @@ %span.light.float-right = boolean_to_icon gravatar_enabled? - omniauth = "OmniAuth" - %p{ "aria-label" => "#{omniauth}: status " + (Gitlab.config.omniauth.enabled ? "on" : "off") } + %p{ "aria-label" => "#{omniauth}: status " + (Gitlab::Auth.omniauth_enabled? ? "on" : "off") } = omniauth %span.light.float-right - = boolean_to_icon Gitlab.config.omniauth.enabled + = boolean_to_icon Gitlab::Auth.omniauth_enabled? - reply_email = "Reply by email" %p{ "aria-label" => "#{reply_email}: status " + (Gitlab::IncomingEmail.enabled? ? "on" : "off") } = reply_email diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 62b7a4cbd07aabe2813b96676e042721771bafea..62be38e9dd2f8bdd1547fb4376770b4eaeadaeea 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -11,7 +11,6 @@ - add_to_breadcrumbs _("Runners"), admin_runners_path - breadcrumb_title "##{@runner.id}" -- @no_container = true - if @runner.instance_type? .bs-callout.bs-callout-success diff --git a/app/views/import/bitbucket_server/new.html.haml b/app/views/import/bitbucket_server/new.html.haml index 11bd53516a698cf1dbd225150a0e1bbf1e7e8ab4..12e0b917de572f33734db0bd5aac8f9ff2ff6655 100644 --- a/app/views/import/bitbucket_server/new.html.haml +++ b/app/views/import/bitbucket_server/new.html.haml @@ -13,7 +13,7 @@ .form-group.row = label_tag :bitbucket_server_url, 'Bitbucket Server URL', class: 'col-form-label col-md-2' .col-md-4 - = text_field_tag :bitbucket_server_url, '', class: 'form-control append-right-8', placeholder: _('URL for Bitbucket Server'), size: 40 + = text_field_tag :bitbucket_server_url, '', class: 'form-control append-right-8', placeholder: _('https://your-bitbucket-server'), size: 40 .form-group.row = label_tag :bitbucket_server_url, 'Username', class: 'col-form-label col-md-2' .col-md-4 diff --git a/app/views/layouts/_recaptcha_verification.html.haml b/app/views/layouts/_recaptcha_verification.html.haml index e6f87ddd383294e3873e2544a4b2dc8651971ef5..3405fb9d5eff309d36a582e3035e7c031f799dc2 100644 --- a/app/views/layouts/_recaptcha_verification.html.haml +++ b/app/views/layouts/_recaptcha_verification.html.haml @@ -1,10 +1,10 @@ - humanized_resource_name = spammable.class.model_name.human.downcase %h3.page-title - Anti-spam verification + = _('Anti-spam verification') %hr %p - #{"We detected potential spam in the #{humanized_resource_name}. Please solve the reCAPTCHA to proceed."} + = _("We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed.") % { humanized_resource_name: humanized_resource_name } = render 'shared/recaptcha_form', spammable: spammable diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 91f796adb2ea6b8e3d489af37c2cceab37432494..556ad8cf306a74314d731012802b73e20b5bd692 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -1,7 +1,7 @@ - if controller.controller_path =~ /^groups/ && @group.persisted? - - label = 'This group' + - label = _('This group') - if controller.controller_path =~ /^projects/ && @project.persisted? - - label = 'This project' + - label = _('This project') - if @group && @group.persisted? && @group.path - group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) } - if @project && @project.persisted? @@ -13,21 +13,21 @@ .location-badge= label .search-input-wrap .dropdown{ data: { url: search_autocomplete_path } } - = search_field_tag 'search', nil, placeholder: 'Search', + = search_field_tag 'search', nil, placeholder: _('Search'), class: 'search-input dropdown-menu-toggle no-outline js-search-dashboard-options', spellcheck: false, tabindex: '1', autocomplete: 'off', data: { issues_path: issues_dashboard_path, mr_path: merge_requests_dashboard_path }, - aria: { label: 'Search' } + aria: { label: _('Search') } %button.hidden.js-dropdown-search-toggle{ type: 'button', data: { toggle: 'dropdown' } } .dropdown-menu.dropdown-select = dropdown_content do %ul %li.dropdown-menu-empty-item %a - Loading... + = _('Loading...') = dropdown_loading = sprite_icon('search', size: 16, css_class: 'search-icon') = sprite_icon('close', size: 16, css_class: 'clear-icon js-clear-input') diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index 8595157a9971d71114f0af3e0a8e40dadb33a412..31259b8ac258688f143c078e24caec896f86a2f7 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -1,5 +1,5 @@ -- page_title "Admin Area" -- header_title "Admin Area", admin_root_path +- page_title _("Admin Area") +- header_title _("Admin Area"), admin_root_path - nav "admin" - @left_sidebar = true diff --git a/app/views/layouts/dashboard.html.haml b/app/views/layouts/dashboard.html.haml index cb96bcc2cf4db75c8ab2b8684980acbf62ef6a9f..489ef245a4d0f8478e2f0bbdc5f257d811d4a2c4 100644 --- a/app/views/layouts/dashboard.html.haml +++ b/app/views/layouts/dashboard.html.haml @@ -1,5 +1,5 @@ -- page_title "Dashboard" -- header_title "Dashboard", root_path unless header_title +- page_title _("Dashboard") +- header_title _("Dashboard"), root_path unless header_title - sidebar "dashboard" = render template: "layouts/application" diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 81f356155550e5fb6d0f3908c545792fa5fe1d14..43bd07679ba8657e0b30f64a1653c727040706c8 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -17,12 +17,11 @@ - if current_appearance&.description? = brand_text - else - %h3 Open source software to collaborate on code + %h3 + = _('Open source software to collaborate on code') %p - Manage Git repositories with fine-grained access controls that keep your code secure. - Perform code reviews and enhance collaboration with merge requests. - Each project can also have an issue tracker and a wiki. + = _('Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki.') - if Gitlab::CurrentSettings.sign_in_text.present? = markdown_field(Gitlab::CurrentSettings.current_application_settings, :sign_in_text) @@ -32,6 +31,6 @@ %hr.footer-fixed .container.footer-container .footer-links - = link_to "Explore", explore_root_path - = link_to "Help", help_path - = link_to "About GitLab", "https://about.gitlab.com/" + = link_to _("Explore"), explore_root_path + = link_to _("Help"), help_path + = link_to _("About GitLab"), "https://about.gitlab.com/" diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml index 52805e0da739ab527095099aab5e5c9618f085d0..663e5b24368f248d56684cc9d944ae105cdbb83b 100644 --- a/app/views/layouts/devise_empty.html.haml +++ b/app/views/layouts/devise_empty.html.haml @@ -12,6 +12,6 @@ %hr .container .footer-links - = link_to "Explore", explore_root_path - = link_to "Help", help_path - = link_to "About GitLab", "https://about.gitlab.com/" + = link_to _("Explore"), explore_root_path + = link_to _("Help"), help_path + = link_to _("About GitLab"), "https://about.gitlab.com/" diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml index df65792be735a769a850a2ffe993855e1f07113d..2ab9e55441b785ead78efdadf185e60e7b5c6c23 100644 --- a/app/views/layouts/explore.html.haml +++ b/app/views/layouts/explore.html.haml @@ -1,5 +1,5 @@ -- page_title "Explore" +- page_title = _("Explore") - unless current_user - - header_title "Explore GitLab", explore_root_path + - header_title = _("Explore GitLab"), explore_root_path = render template: "layouts/application" diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml index 66b115e36de69e0d332051f3965bfbeb9a3b53fa..14c5f0ce04c40f0d7f60a6fe6d0e730271defd72 100644 --- a/app/views/layouts/group_settings.html.haml +++ b/app/views/layouts/group_settings.html.haml @@ -1,4 +1,4 @@ -- page_title "Settings" +- page_title = _("Settings") - nav "group" = render template: "layouts/group" diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 3aa8eb18bf33b2ae9dd6572f9bb12630513700d1..97c04dda8cb7494ffd3700bd88904d171a0b3ed1 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -4,7 +4,7 @@ .header-content .title-container %h1.title - = link_to root_path, title: 'Dashboard', id: 'logo' do + = link_to root_path, title: _('Dashboard'), id: 'logo' do = brand_header_logo - logo_text = brand_header_logo_type - if logo_text.present? @@ -24,26 +24,26 @@ %li.nav-item.d-none.d-sm-none.d-md-block.m-auto = render 'layouts/search' unless current_controller?(:search) %li.nav-item.d-inline-block.d-sm-none.d-md-none - = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = link_to search_path, title: _('Search'), aria: { label: _("Search") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = sprite_icon('search', size: 16) - if header_link?(:issues) = nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do - = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues', aria: { label: _("Issues") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = sprite_icon('issues', size: 16) - issues_count = assigned_issuables_count(:issues) %span.badge.badge-pill.issues-count{ class: ('hidden' if issues_count.zero?) } = number_with_delimiter(issues_count) - if header_link?(:merge_requests) = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do - = link_to assigned_mrs_dashboard_path, title: 'Merge requests', class: 'dashboard-shortcuts-merge_requests', aria: { label: "Merge requests" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = link_to assigned_mrs_dashboard_path, title: _('Merge requests'), class: 'dashboard-shortcuts-merge_requests', aria: { label: _("Merge requests") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = sprite_icon('git-merge', size: 16) - merge_requests_count = assigned_issuables_count(:merge_requests) %span.badge.badge-pill.merge-requests-count{ class: ('hidden' if merge_requests_count.zero?) } = number_with_delimiter(merge_requests_count) - if header_link?(:todos) = nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do - = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = link_to dashboard_todos_path, title: _('Todos'), aria: { label: _("Todos") }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = sprite_icon('todo-done', size: 16) %span.badge.badge-pill.todos-count{ class: ('hidden' if todos_pending_count.zero?) } = todos_count_format(todos_pending_count) @@ -56,16 +56,16 @@ = render 'layouts/header/current_user_dropdown' - if header_link?(:admin_impersonation) %li.nav-item.impersonation - = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: "Stop impersonation", aria: { label: 'Stop impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do + = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: _("Stop impersonation"), aria: { label: _('Stop impersonation') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = icon('user-secret') - if header_link?(:sign_in) %li.nav-item %div - - sign_in_text = allow_signup? ? 'Sign in / Register' : 'Sign in' + - sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in') = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in' %button.navbar-toggler.d-block.d-sm-none{ type: 'button' } - %span.sr-only Toggle navigation + %span.sr-only= _("Toggle navigation") = sprite_icon('more', size: 12, css_class: 'more-icon js-navbar-toggle-right') = sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left') diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml index 792291bde7519fa9cbd22704ecbf02d8ebe99d94..e134f416c708c2901d24c98140eee4c723e501d6 100644 --- a/app/views/layouts/header/_new_dropdown.haml +++ b/app/views/layouts/header/_new_dropdown.haml @@ -1,5 +1,5 @@ %li.header-new.dropdown - = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do + = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: _("New..."), ref: 'tooltip', aria: { label: _("New...") }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do = sprite_icon('plus-square', size: 16) = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu.dropdown-menu-right @@ -8,13 +8,14 @@ - create_group_project = can?(current_user, :create_projects, @group) - create_group_subgroup = can?(current_user, :create_subgroup, @group) - if create_group_project || create_group_subgroup - %li.dropdown-bold-header This group + %li.dropdown-bold-header + = _('This group') - if create_group_project %li.header-new-group-project - = link_to 'New project', new_project_path(namespace_id: @group.id) + = link_to _('New project'), new_project_path(namespace_id: @group.id) - if create_group_subgroup %li - = link_to 'New subgroup', new_group_path(parent_id: @group.id) + = link_to _('New subgroup'), new_group_path(parent_id: @group.id) %li.divider %li.dropdown-bold-header GitLab @@ -23,23 +24,24 @@ - merge_project = merge_request_source_project_for_project(@project) - create_project_snippet = can?(current_user, :create_project_snippet, @project) - if create_project_issue || merge_project || create_project_snippet - %li.dropdown-bold-header This project + %li.dropdown-bold-header + = _('This project') - if create_project_issue %li - = link_to 'New issue', new_project_issue_path(@project) + = link_to _('New issue'), new_project_issue_path(@project) - if merge_project %li - = link_to 'New merge request', project_new_merge_request_path(merge_project) + = link_to _('New merge request'), project_new_merge_request_path(merge_project) - if create_project_snippet %li.header-new-project-snippet - = link_to 'New snippet', new_project_snippet_path(@project) + = link_to _('New snippet'), new_project_snippet_path(@project) %li.divider %li.dropdown-bold-header GitLab - if current_user.can_create_project? %li - = link_to 'New project', new_project_path, class: 'qa-global-new-project-link' + = link_to _('New project'), new_project_path, class: 'qa-global-new-project-link' - if current_user.can_create_group? %li - = link_to 'New group', new_group_path + = link_to _('New group'), new_group_path %li - = link_to 'New snippet', new_snippet_path + = link_to _('New snippet'), new_snippet_path diff --git a/app/views/layouts/help.html.haml b/app/views/layouts/help.html.haml index 78927bfffcd27efc910a9cd558ed06b46192c650..a913bea0c93f366e78048cacb33cf2dbd80d7fb4 100644 --- a/app/views/layouts/help.html.haml +++ b/app/views/layouts/help.html.haml @@ -1,5 +1,5 @@ -- @breadcrumb_title = "Help" -- page_title "Help" -- header_title "Help", help_path +- @breadcrumb_title = _("Help") +- page_title _("Help") +- header_title _("Help"), help_path = render template: "layouts/application" diff --git a/app/views/layouts/koding.html.haml b/app/views/layouts/koding.html.haml index 22319bba745f84a8c597eab946aca8a455c2a1f9..45ccd38f687067361c11d8703bc07400a28ee717 100644 --- a/app/views/layouts/koding.html.haml +++ b/app/views/layouts/koding.html.haml @@ -1,5 +1,5 @@ -- page_title "Koding" -- page_description "Koding Dashboard" -- header_title "Koding", koding_path +- page_title _("Koding") +- page_description _("Koding Dashboard") +- header_title _("Koding"), koding_path = render template: "layouts/application" diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb index 8e20c4a4b2ac01aa9f20ded0fb09e1e321b1f7a1..8e11174f8d761a46a345909cda3a8b3ccedfcb2f 100644 --- a/app/views/layouts/mailer.text.erb +++ b/app/views/layouts/mailer.text.erb @@ -1,4 +1,4 @@ <%= yield -%> -- <%# signature marker %> -You're receiving this email because of your account on <%= Gitlab.config.gitlab.host %>. +<%= _("You're receiving this email because of your account on %{host}.") % { host: Gitlab.config.gitlab.host } %> diff --git a/app/views/layouts/mailer/devise.html.haml b/app/views/layouts/mailer/devise.html.haml index 054b2c2fa262edff35d009c9ddbedf4de30ce9d4..beaaaa5cd682517a13d4a58ef059fc84b4f20043 100644 --- a/app/views/layouts/mailer/devise.html.haml +++ b/app/views/layouts/mailer/devise.html.haml @@ -3,17 +3,17 @@ %tr %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:13px;line-height:1.6;color:#5c5c5c;" } %div - Everyone can contribute + = _('Everyone can contribute') %div - = link_to 'Blog', 'https://about.gitlab.com/blog/', style: "color:#3777b0;text-decoration:none;" + = link_to _('Blog'), 'https://about.gitlab.com/blog/', style: "color:#3777b0;text-decoration:none;" · - = link_to 'Twitter', 'https://twitter.com/gitlab', style: "color:#3777b0;text-decoration:none;" + = link_to _('Twitter'), 'https://twitter.com/gitlab', style: "color:#3777b0;text-decoration:none;" · - = link_to 'Facebook', 'https://www.facebook.com/gitlab/', style: "color:#3777b0;text-decoration:none;" + = link_to _('Facebook'), 'https://www.facebook.com/gitlab/', style: "color:#3777b0;text-decoration:none;" · - = link_to 'YouTube', 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg', style: "color:#3777b0;text-decoration:none;" + = link_to _('YouTube'), 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg', style: "color:#3777b0;text-decoration:none;" · - = link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com', style: "color:#3777b0;text-decoration:none;" + = link_to _('LinkedIn'), 'https://www.linkedin.com/company/gitlab-com', style: "color:#3777b0;text-decoration:none;" = render layout: 'layouts/mailer' do %tr diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml index 002922e13f12d48daa2d230f23a15dc1396582f4..f53bd2b5e4ddacacb4f108cecce1f1cdc416a85f 100644 --- a/app/views/layouts/nav/_breadcrumbs.html.haml +++ b/app/views/layouts/nav/_breadcrumbs.html.haml @@ -5,7 +5,7 @@ .breadcrumbs-container - if defined?(@left_sidebar) = button_tag class: 'toggle-mobile-nav', type: 'button' do - %span.sr-only Open sidebar + %span.sr-only= _("Open sidebar") = icon ('bars') .breadcrumbs-links.js-title-container %ul.list-unstyled.breadcrumbs-list.js-breadcrumbs-list diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 4029287fc0eb21bd76b23fcc44e2bb8c235c35bf..a71a4b13a7eac377b34545a305a15f200b2eb13b 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -2,7 +2,7 @@ - if dashboard_nav_link?(:projects) = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects qa-projects-dropdown" }) do %button{ type: 'button', data: { toggle: "dropdown" } } - Projects + = _('Projects') = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu.frequent-items-dropdown-menu = render "layouts/nav/projects_dropdown/show" @@ -10,68 +10,68 @@ - if dashboard_nav_link?(:groups) = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { id: 'nav-groups-dropdown', class: "home dropdown header-groups qa-groups-dropdown" }) do %button{ type: 'button', data: { toggle: "dropdown" } } - Groups + = _('Groups') = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu.frequent-items-dropdown-menu = render "layouts/nav/groups_dropdown/show" - if dashboard_nav_link?(:activity) = nav_link(path: 'dashboard#activity', html_options: { class: "d-none d-lg-block d-xl-block" }) do - = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do - Activity + = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: _('Activity') do + = _('Activity') - if dashboard_nav_link?(:milestones) = nav_link(controller: 'dashboard/milestones', html_options: { class: "d-none d-lg-block d-xl-block" }) do - = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do - Milestones + = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: _('Milestones') do + = _('Milestones') - if dashboard_nav_link?(:snippets) = nav_link(controller: 'dashboard/snippets', html_options: { class: "d-none d-lg-block d-xl-block" }) do - = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do - Snippets + = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: _('Snippets') do + = _('Snippets') - if any_dashboard_nav_link?([:groups, :milestones, :activity, :snippets]) %li.header-more.dropdown.d-lg-none.d-xl-none %a{ href: "#", data: { toggle: "dropdown" } } - More + = _('More') = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu %ul - if dashboard_nav_link?(:activity) = nav_link(path: 'dashboard#activity') do - = link_to activity_dashboard_path, title: 'Activity' do - Activity + = link_to activity_dashboard_path, title: _('Activity') do + = _('Activity') - if dashboard_nav_link?(:milestones) = nav_link(controller: 'dashboard/milestones') do - = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do - Milestones + = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: _('Milestones') do + = _('Milestones') - if dashboard_nav_link?(:snippets) = nav_link(controller: 'dashboard/snippets') do - = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do - Snippets + = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: _('Snippets') do + = _('Snippets') -# Shortcut to Dashboard > Projects - if dashboard_nav_link?(:projects) %li.hidden - = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do - Projects + = link_to dashboard_projects_path, title: _('Projects'), class: 'dashboard-shortcuts-projects' do + = _('Projects') - if current_controller?('ide') %li.line-separator.d-none.d-sm-block = nav_link(controller: 'ide') do - = link_to '#', class: 'dashboard-shortcuts-web-ide', title: 'Web IDE' do - Web IDE + = link_to '#', class: 'dashboard-shortcuts-web-ide', title: _('Web IDE') do + = _('Web IDE') - if current_user.admin? || Gitlab::Sherlock.enabled? %li.line-separator.d-none.d-sm-block - if current_user.admin? = nav_link(controller: 'admin/dashboard') do - = link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: 'Admin area', aria: { label: "Admin area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: _('Admin area'), aria: { label: _("Admin area") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = sprite_icon('admin', size: 18) - if Gitlab::Sherlock.enabled? %li - = link_to sherlock_transactions_path, class: 'admin-icon', title: 'Sherlock Transactions', + = link_to sherlock_transactions_path, class: 'admin-icon', title: _('Sherlock Transactions'), data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('tachometer fw') diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml index 50bde9d1754d95a0613883c7218e52b3e4e1050c..7d18cd8978b1b3ab275a17b5e6e314e01dd7d9a9 100644 --- a/app/views/layouts/nav/_explore.html.haml +++ b/app/views/layouts/nav/_explore.html.haml @@ -1,15 +1,15 @@ %ul.list-unstyled.navbar-sub-nav - if explore_nav_link?(:projects) = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do - = link_to explore_root_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do - Projects + = link_to explore_root_path, title: _('Projects'), class: 'dashboard-shortcuts-projects' do + = _('Projects') - if explore_nav_link?(:groups) = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do - = link_to explore_groups_path, title: 'Groups', class: 'dashboard-shortcuts-groups' do - Groups + = link_to explore_groups_path, title: _('Groups'), class: 'dashboard-shortcuts-groups' do + = _('Groups') - if explore_nav_link?(:snippets) = nav_link(controller: :snippets) do - = link_to explore_snippets_path, title: 'Snippets', class: 'dashboard-shortcuts-snippets' do - Snippets + = link_to explore_snippets_path, title: _('Snippets'), class: 'dashboard-shortcuts-snippets' do + = _('Snippets') %li - = link_to "Help", help_path, title: 'About GitLab CE' + = link_to _("Help"), help_path, title: _('About GitLab CE') diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml index 4c73da4c75bb1e9e0751592f332ae7cd04897c1d..0047efa363d2a23ce706d0d29964a21bb6862183 100644 --- a/app/views/layouts/nav/sidebar/_admin.html.haml +++ b/app/views/layouts/nav/sidebar/_admin.html.haml @@ -1,142 +1,143 @@ .nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) } .nav-sidebar-inner-scroll .context-header - = link_to admin_root_path, title: 'Admin Overview' do + = link_to admin_root_path, title: _('Admin Overview') do .avatar-container.s40.settings-avatar = sprite_icon('admin', size: 24) - .sidebar-context-title Admin Area + .sidebar-context-title + = _('Admin Area') %ul.sidebar-top-level-items = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers cohorts conversational_development_index), html_options: {class: 'home'}) do = link_to admin_root_path, class: 'shortcuts-tree' do .nav-icon-container = sprite_icon('overview') %span.nav-item-name - Overview + = _('Overview') %ul.sidebar-sub-level-items = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers cohorts conversational_development_index), html_options: { class: "fly-out-top-item" } ) do = link_to admin_root_path do %strong.fly-out-top-item-name - #{ _('Overview') } + = _('Overview') %li.divider.fly-out-top-item = nav_link(controller: :dashboard, html_options: {class: 'home'}) do - = link_to admin_root_path, title: 'Overview' do + = link_to admin_root_path, title: _('Overview') do %span - Dashboard + = _('Dashboard') = nav_link(controller: [:admin, :projects]) do - = link_to admin_projects_path, title: 'Projects' do + = link_to admin_projects_path, title: _('Projects') do %span - Projects + = _('Projects') = nav_link(controller: :users) do - = link_to admin_users_path, title: 'Users' do + = link_to admin_users_path, title: _('Users') do %span - Users + = _('Users') = nav_link(controller: :groups) do - = link_to admin_groups_path, title: 'Groups' do + = link_to admin_groups_path, title: _('Groups') do %span - Groups + = _('Groups') = nav_link path: 'jobs#index' do - = link_to admin_jobs_path, title: 'Jobs' do + = link_to admin_jobs_path, title: _('Jobs') do %span - Jobs + = _('Jobs') = nav_link path: ['runners#index', 'runners#show'] do - = link_to admin_runners_path, title: 'Runners' do + = link_to admin_runners_path, title: _('Runners') do %span - Runners + = _('Runners') = nav_link(controller: :gitaly_servers) do = link_to admin_gitaly_servers_path, title: 'Gitaly Servers' do %span - Gitaly Servers + = _('Gitaly Servers') = nav_link path: 'cohorts#index' do - = link_to admin_cohorts_path, title: 'Cohorts' do + = link_to admin_cohorts_path, title: _('Cohorts') do %span - Cohorts + = _('Cohorts') = nav_link(controller: :conversational_development_index) do - = link_to admin_conversational_development_index_path, title: 'ConvDev Index' do + = link_to admin_conversational_development_index_path, title: _('ConvDev Index') do %span - ConvDev Index + = _('ConvDev Index') = nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles)) do = link_to admin_system_info_path do .nav-icon-container = sprite_icon('monitor') %span.nav-item-name - Monitoring + = _('Monitoring') %ul.sidebar-sub-level-items = nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles), html_options: { class: "fly-out-top-item" } ) do = link_to admin_system_info_path do %strong.fly-out-top-item-name - #{ _('Monitoring') } + = _('Monitoring') %li.divider.fly-out-top-item = nav_link(controller: :system_info) do - = link_to admin_system_info_path, title: 'System Info' do + = link_to admin_system_info_path, title: _('System Info') do %span - System Info + = _('System Info') = nav_link(controller: :background_jobs) do - = link_to admin_background_jobs_path, title: 'Background Jobs' do + = link_to admin_background_jobs_path, title: _('Background Jobs') do %span - Background Jobs + = _('Background Jobs') = nav_link(controller: :logs) do - = link_to admin_logs_path, title: 'Logs' do + = link_to admin_logs_path, title: _('Logs') do %span - Logs + = _('Logs') = nav_link(controller: :health_check) do - = link_to admin_health_check_path, title: 'Health Check' do + = link_to admin_health_check_path, title: _('Health Check') do %span - Health Check + = _('Health Check') = nav_link(controller: :requests_profiles) do - = link_to admin_requests_profiles_path, title: 'Requests Profiles' do + = link_to admin_requests_profiles_path, title: _('Requests Profiles') do %span - Requests Profiles + = _('Requests Profiles') = nav_link(controller: :broadcast_messages) do = link_to admin_broadcast_messages_path do .nav-icon-container = sprite_icon('messages') %span.nav-item-name - Messages + = _('Messages') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :broadcast_messages, html_options: { class: "fly-out-top-item" } ) do = link_to admin_broadcast_messages_path do %strong.fly-out-top-item-name - #{ _('Messages') } + = _('Messages') = nav_link(controller: [:hooks, :hook_logs]) do = link_to admin_hooks_path do .nav-icon-container = sprite_icon('hook') %span.nav-item-name - System Hooks + = _('System Hooks') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: [:hooks, :hook_logs], html_options: { class: "fly-out-top-item" } ) do = link_to admin_hooks_path do %strong.fly-out-top-item-name - #{ _('System Hooks') } + = _('System Hooks') = nav_link(controller: :applications) do = link_to admin_applications_path do .nav-icon-container = sprite_icon('applications') %span.nav-item-name - Applications + = _('Applications') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :applications, html_options: { class: "fly-out-top-item" } ) do = link_to admin_applications_path do %strong.fly-out-top-item-name - #{ _('Applications') } + = _('Applications') = nav_link(controller: :abuse_reports) do = link_to admin_abuse_reports_path do .nav-icon-container = sprite_icon('slight-frown') %span.nav-item-name - Abuse Reports + = _('Abuse Reports') %span.badge.badge-pill.count= number_with_delimiter(AbuseReport.count(:all)) %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :abuse_reports, html_options: { class: "fly-out-top-item" } ) do = link_to admin_abuse_reports_path do %strong.fly-out-top-item-name - #{ _('Abuse Reports') } + = _('Abuse Reports') %span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(AbuseReport.count(:all)) - if akismet_enabled? @@ -145,71 +146,71 @@ .nav-icon-container = sprite_icon('spam') %span.nav-item-name - Spam Logs + = _('Spam Logs') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :spam_logs, html_options: { class: "fly-out-top-item" } ) do = link_to admin_spam_logs_path do %strong.fly-out-top-item-name - #{ _('Spam Logs') } + = _('Spam Logs') = nav_link(controller: :deploy_keys) do = link_to admin_deploy_keys_path do .nav-icon-container = sprite_icon('key') %span.nav-item-name - Deploy Keys + = _('Deploy Keys') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :deploy_keys, html_options: { class: "fly-out-top-item" } ) do = link_to admin_deploy_keys_path do %strong.fly-out-top-item-name - #{ _('Deploy Keys') } + = _('Deploy Keys') = nav_link(controller: :services) do = link_to admin_application_settings_services_path do .nav-icon-container = sprite_icon('template') %span.nav-item-name - Service Templates + = _('Service Templates') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :services, html_options: { class: "fly-out-top-item" } ) do = link_to admin_application_settings_services_path do %strong.fly-out-top-item-name - #{ _('Service Templates') } + = _('Service Templates') = nav_link(controller: :labels) do = link_to admin_labels_path do .nav-icon-container = sprite_icon('labels') %span.nav-item-name - Labels + = _('Labels') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :labels, html_options: { class: "fly-out-top-item" } ) do = link_to admin_labels_path do %strong.fly-out-top-item-name - #{ _('Labels') } + = _('Labels') = nav_link(controller: :appearances) do = link_to admin_appearances_path do .nav-icon-container = sprite_icon('appearance') %span.nav-item-name - Appearance + = _('Appearance') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :appearances, html_options: { class: "fly-out-top-item" } ) do = link_to admin_appearances_path do %strong.fly-out-top-item-name - #{ _('Appearance') } + = _('Appearance') = nav_link(controller: :application_settings) do = link_to admin_application_settings_path do .nav-icon-container = sprite_icon('settings') %span.nav-item-name - Settings + = _('Settings') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :application_settings, html_options: { class: "fly-out-top-item" } ) do = link_to admin_application_settings_path do %strong.fly-out-top-item-name - #{ _('Settings') } + = _('Settings') = render 'shared/sidebar_toggle_button' diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 39a033337ff2efd78cc75903714ac7cbd7df3d02..0a3b5ec7eeaa74a93b6527cb4e89661c771a90dd 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -17,24 +17,24 @@ .nav-icon-container = sprite_icon('project') %span.nav-item-name - Overview + = _('Overview') %ul.sidebar-sub-level-items = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do = link_to group_path(@group) do %strong.fly-out-top-item-name - #{ _('Overview') } + = _('Overview') %li.divider.fly-out-top-item = nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do - = link_to group_path(@group), title: 'Group details' do + = link_to group_path(@group), title: _('Group details') do %span - Details + = _('Details') - if group_sidebar_link?(:activity) = nav_link(path: 'groups#activity') do - = link_to activity_group_path(@group), title: 'Activity' do + = link_to activity_group_path(@group), title: _('Activity') do %span - Activity + = _('Activity') - if group_sidebar_link?(:issues) = nav_link(path: issues_sub_menu_items) do @@ -42,21 +42,21 @@ .nav-icon-container = sprite_icon('issues') %span.nav-item-name - Issues + = _('Issues') %span.badge.badge-pill.count= number_with_delimiter(issues_count) %ul.sidebar-sub-level-items = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do = link_to issues_group_path(@group) do %strong.fly-out-top-item-name - #{ _('Issues') } + = _('Issues') %span.badge.badge-pill.count.issue_counter.fly-out-badge= number_with_delimiter(issues_count) %li.divider.fly-out-top-item = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do - = link_to issues_group_path(@group), title: 'List' do + = link_to issues_group_path(@group), title: _('List') do %span - List + = _('List') - if group_sidebar_link?(:boards) = nav_link(path: ['boards#index', 'boards#show']) do @@ -66,15 +66,15 @@ - if group_sidebar_link?(:labels) = nav_link(path: 'labels#index') do - = link_to group_labels_path(@group), title: 'Labels' do + = link_to group_labels_path(@group), title: _('Labels') do %span - Labels + = _('Labels') - if group_sidebar_link?(:milestones) = nav_link(path: 'milestones#index') do - = link_to group_milestones_path(@group), title: 'Milestones' do + = link_to group_milestones_path(@group), title: _('Milestones') do %span - Milestones + = _('Milestones') - if group_sidebar_link?(:merge_requests) = nav_link(path: 'groups#merge_requests') do @@ -82,13 +82,13 @@ .nav-icon-container = sprite_icon('git-merge') %span.nav-item-name - Merge Requests + = _('Merge Requests') %span.badge.badge-pill.count= number_with_delimiter(merge_requests_count) %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do = link_to merge_requests_group_path(@group) do %strong.fly-out-top-item-name - #{ _('Merge Requests') } + = _('Merge Requests') %span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests_count) - if group_sidebar_link?(:group_members) @@ -97,12 +97,12 @@ .nav-icon-container = sprite_icon('users') %span.nav-item-name - Members + = _('Members') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do = link_to group_group_members_path(@group) do %strong.fly-out-top-item-name - #{ _('Members') } + = _('Members') - if group_sidebar_link?(:settings) = nav_link(path: group_nav_link_paths) do @@ -110,17 +110,17 @@ .nav-icon-container = sprite_icon('settings') %span.nav-item-name - Settings + = _('Settings') %ul.sidebar-sub-level-items = nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show], html_options: { class: "fly-out-top-item" } ) do = link_to edit_group_path(@group) do %strong.fly-out-top-item-name - #{ _('Settings') } + = _('Settings') %li.divider.fly-out-top-item = nav_link(path: 'groups#edit') do - = link_to edit_group_path(@group), title: 'General' do + = link_to edit_group_path(@group), title: _('General') do %span - General + = _('General') = nav_link(controller: :badges) do = link_to group_settings_badges_path(@group), title: _('Project Badges') do @@ -129,13 +129,13 @@ = nav_link(path: 'groups#projects') do - = link_to projects_group_path(@group), title: 'Projects' do + = link_to projects_group_path(@group), title: _('Projects') do %span - Projects + = _('Projects') = nav_link(controller: :ci_cd) do - = link_to group_settings_ci_cd_path(@group), title: 'CI / CD' do + = link_to group_settings_ci_cd_path(@group), title: _('CI / CD') do %span - CI / CD + = _('CI / CD') = render 'shared/sidebar_toggle_button' diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml index 6cbd163dd41b93b9af5dc49b37cd655accceca65..94863a3460dca35652b0ab8cde0182f9aa67a3bc 100644 --- a/app/views/layouts/nav/sidebar/_profile.html.haml +++ b/app/views/layouts/nav/sidebar/_profile.html.haml @@ -1,7 +1,7 @@ .nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) } .nav-sidebar-inner-scroll .context-header - = link_to profile_path, title: 'Profile Settings' do + = link_to profile_path, title: _('Profile Settings') do .avatar-container.s40.settings-avatar = sprite_icon('user', size: 24) .sidebar-context-title User Settings @@ -11,145 +11,145 @@ .nav-icon-container = sprite_icon('profile') %span.nav-item-name - Profile + = _('Profile') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(path: 'profiles#show', html_options: { class: "fly-out-top-item" } ) do = link_to profile_path do %strong.fly-out-top-item-name - #{ _('Profile') } + = _('Profile') = nav_link(controller: [:accounts, :two_factor_auths]) do = link_to profile_account_path do .nav-icon-container = sprite_icon('account') %span.nav-item-name - Account + = _('Account') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: [:accounts, :two_factor_auths], html_options: { class: "fly-out-top-item" } ) do = link_to profile_account_path do %strong.fly-out-top-item-name - #{ _('Account') } + = _('Account') - if Gitlab::CurrentSettings.user_oauth_applications? = nav_link(controller: 'oauth/applications') do = link_to applications_profile_path do .nav-icon-container = sprite_icon('applications') %span.nav-item-name - Applications + = _('Applications') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: 'oauth/applications', html_options: { class: "fly-out-top-item" } ) do = link_to applications_profile_path do %strong.fly-out-top-item-name - #{ _('Applications') } + = _('Applications') = nav_link(controller: :chat_names) do = link_to profile_chat_names_path do .nav-icon-container = sprite_icon('comment') %span.nav-item-name - Chat + = _('Chat') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :chat_names, html_options: { class: "fly-out-top-item" } ) do = link_to profile_chat_names_path do %strong.fly-out-top-item-name - #{ _('Chat') } + = _('Chat') = nav_link(controller: :personal_access_tokens) do = link_to profile_personal_access_tokens_path do .nav-icon-container = sprite_icon('token') %span.nav-item-name - Access Tokens + = _('Access Tokens') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :personal_access_tokens, html_options: { class: "fly-out-top-item" } ) do = link_to profile_personal_access_tokens_path do %strong.fly-out-top-item-name - #{ _('Access Tokens') } + = _('Access Tokens') = nav_link(controller: :emails) do = link_to profile_emails_path do .nav-icon-container = sprite_icon('mail') %span.nav-item-name - Emails + = _('Emails') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :emails, html_options: { class: "fly-out-top-item" } ) do = link_to profile_emails_path do %strong.fly-out-top-item-name - #{ _('Emails') } + = _('Emails') - if current_user.allow_password_authentication? = nav_link(controller: :passwords) do = link_to edit_profile_password_path do .nav-icon-container = sprite_icon('lock') %span.nav-item-name - Password + = _('Password') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :passwords, html_options: { class: "fly-out-top-item" } ) do = link_to edit_profile_password_path do %strong.fly-out-top-item-name - #{ _('Password') } + = _('Password') = nav_link(controller: :notifications) do = link_to profile_notifications_path do .nav-icon-container = sprite_icon('notifications') %span.nav-item-name - Notifications + = _('Notifications') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :notifications, html_options: { class: "fly-out-top-item" } ) do = link_to profile_notifications_path do %strong.fly-out-top-item-name - #{ _('Notifications') } + = _('Notifications') = nav_link(controller: :keys) do = link_to profile_keys_path do .nav-icon-container = sprite_icon('key') %span.nav-item-name - SSH Keys + = _('SSH Keys') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :keys, html_options: { class: "fly-out-top-item" } ) do = link_to profile_keys_path do %strong.fly-out-top-item-name - #{ _('SSH Keys') } + = _('SSH Keys') = nav_link(controller: :gpg_keys) do = link_to profile_gpg_keys_path do .nav-icon-container = sprite_icon('key-2') %span.nav-item-name - GPG Keys + = _('GPG Keys') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :gpg_keys, html_options: { class: "fly-out-top-item" } ) do = link_to profile_gpg_keys_path do %strong.fly-out-top-item-name - #{ _('GPG Keys') } + = _('GPG Keys') = nav_link(controller: :preferences) do = link_to profile_preferences_path do .nav-icon-container = sprite_icon('preferences') %span.nav-item-name - Preferences + = _('Preferences') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :preferences, html_options: { class: "fly-out-top-item" } ) do = link_to profile_preferences_path do %strong.fly-out-top-item-name - #{ _('Preferences') } + = _('Preferences') = nav_link(controller: :active_sessions) do = link_to profile_active_sessions_path do .nav-icon-container = sprite_icon('monitor-lines') %span.nav-item-name - Active Sessions + = _('Active Sessions') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(controller: :active_sessions, html_options: { class: "fly-out-top-item" } ) do = link_to profile_active_sessions_path do %strong.fly-out-top-item-name - #{ _('Active Sessions') } + = _('Active Sessions') = nav_link(path: 'profiles#audit_log') do = link_to audit_log_profile_path do .nav-icon-container = sprite_icon('log') %span.nav-item-name - Authentication log + = _('Authentication log') %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(path: 'profiles#audit_log', html_options: { class: "fly-out-top-item" } ) do = link_to audit_log_profile_path do %strong.fly-out-top-item-name - #{ _('Authentication Log') } + = _('Authentication Log') = render 'shared/sidebar_toggle_button' diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 33de74dbaa24486b679885269e945f21573c78f6..0ec61df1f0a12510f28bf681df3e0bf1546e6436 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -105,7 +105,7 @@ = number_with_delimiter(@project.open_issues_count(current_user)) %li.divider.fly-out-top-item = nav_link(controller: :issues, action: :index) do - = link_to project_issues_path(@project), title: 'Issues' do + = link_to project_issues_path(@project), title: _('Issues') do %span = _('List') @@ -115,14 +115,14 @@ = boards_link_text = nav_link(controller: :labels) do - = link_to project_labels_path(@project), title: 'Labels' do + = link_to project_labels_path(@project), title: _('Labels') do %span = _('Labels') = render_if_exists 'projects/sidebar/issues_service_desk' = nav_link(controller: :milestones) do - = link_to project_milestones_path(@project), title: 'Milestones', class: 'qa-milestones-link' do + = link_to project_milestones_path(@project), title: _('Milestones'), class: 'qa-milestones-link' do %span = _('Milestones') - if project_nav_tab? :external_issue_tracker @@ -172,25 +172,25 @@ %li.divider.fly-out-top-item - if project_nav_tab? :pipelines = nav_link(path: ['pipelines#index', 'pipelines#show']) do - = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do + = link_to project_pipelines_path(@project), title: _('Pipelines'), class: 'shortcuts-pipelines' do %span = _('Pipelines') - if project_nav_tab? :builds = nav_link(controller: [:jobs, :artifacts]) do - = link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do + = link_to project_jobs_path(@project), title: _('Jobs'), class: 'shortcuts-builds' do %span = _('Jobs') - if project_nav_tab? :pipelines = nav_link(controller: :pipeline_schedules) do - = link_to pipeline_schedules_path(@project), title: 'Schedules', class: 'shortcuts-builds' do + = link_to pipeline_schedules_path(@project), title: _('Schedules'), class: 'shortcuts-builds' do %span = _('Schedules') - if @project.feature_available?(:builds, current_user) && !@project.empty_repo? = nav_link(path: 'pipelines#charts') do - = link_to charts_project_pipelines_path(@project), title: 'Charts', class: 'shortcuts-pipelines-charts' do + = link_to charts_project_pipelines_path(@project), title: _('Charts'), class: 'shortcuts-pipelines-charts' do %span = _('Charts') @@ -242,7 +242,7 @@ %p= _('Allows you to add and manage Kubernetes clusters.') %p = _('Protip:') - = link_to 'Auto DevOps', help_page_path('topics/autodevops/index.md') + = link_to _('Auto DevOps'), help_page_path('topics/autodevops/index.md') %span= _('uses Kubernetes clusters to deploy your code!') %hr %button.btn.btn-create.btn-sm.dismiss-feature-highlight{ type: 'button' } @@ -305,11 +305,11 @@ = _('Settings') %li.divider.fly-out-top-item = nav_link(path: %w[projects#edit]) do - = link_to edit_project_path(@project), title: 'General' do + = link_to edit_project_path(@project), title: _('General') do %span = _('General') = nav_link(controller: :project_members) do - = link_to project_project_members_path(@project), title: 'Members' do + = link_to project_project_members_path(@project), title: _('Members') do %span = _('Members') - if can_edit @@ -319,21 +319,21 @@ = _('Badges') - if can_edit = nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do - = link_to project_settings_integrations_path(@project), title: 'Integrations' do + = link_to project_settings_integrations_path(@project), title: _('Integrations') do %span = _('Integrations') = nav_link(controller: :repository) do - = link_to project_settings_repository_path(@project), title: 'Repository' do + = link_to project_settings_repository_path(@project), title: _('Repository') do %span = _('Repository') - if @project.feature_available?(:builds, current_user) = nav_link(controller: :ci_cd) do - = link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do + = link_to project_settings_ci_cd_path(@project), title: _('CI / CD') do %span = _('CI / CD') - if @project.pages_available? = nav_link(controller: :pages) do - = link_to project_pages_path(@project), title: 'Pages' do + = link_to project_pages_path(@project), title: _('Pages') do %span = _('Pages') @@ -341,7 +341,7 @@ - else = nav_link(controller: :project_members) do - = link_to project_settings_members_path(@project), title: 'Members', class: 'shortcuts-tree' do + = link_to project_settings_members_path(@project), title: _('Members'), class: 'shortcuts-tree' do .nav-icon-container = sprite_icon('users') %span.nav-item-name @@ -356,41 +356,41 @@ -# Shortcut to Project > Activity %li.hidden - = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do + = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do %span - Activity + = _('Activity') -# Shortcut to Repository > Graph (formerly, Network) - if project_nav_tab? :network %li.hidden - = link_to project_network_path(@project, current_ref), title: 'Network', class: 'shortcuts-network' do - Graph + = link_to project_network_path(@project, current_ref), title: _('Network'), class: 'shortcuts-network' do + = _('Graph') -# Shortcut to Repository > Charts (formerly, top-nav item "Graphs") - unless @project.empty_repo? %li.hidden - = link_to charts_project_graph_path(@project, current_ref), title: 'Charts', class: 'shortcuts-repository-charts' do - Charts + = link_to charts_project_graph_path(@project, current_ref), title: _('Charts'), class: 'shortcuts-repository-charts' do + = _('Charts') -# Shortcut to Issues > New Issue - if project_nav_tab?(:issues) %li.hidden = link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do - Create a new issue + = _('Create a new issue') -# Shortcut to Pipelines > Jobs - if project_nav_tab? :builds %li.hidden - = link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do - Jobs + = link_to project_jobs_path(@project), title: _('Jobs'), class: 'shortcuts-builds' do + = _('Jobs') -# Shortcut to commits page - if project_nav_tab? :commits %li.hidden - = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do - Commits + = link_to project_commits_path(@project), title: _('Commits'), class: 'shortcuts-commits' do + = _('Commits') -# Shortcut to issue boards - if project_nav_tab?(:issues) %li.hidden - = link_to 'Issue Boards', project_boards_path(@project), title: 'Issue Boards', class: 'shortcuts-issue-boards' + = link_to _('Issue Boards'), project_boards_path(@project), title: _('Issue Boards'), class: 'shortcuts-issue-boards' diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index ab8b1271212fb7bf93a48aad898d8a0bb18d0098..31e02f1ee1955475c875137884e48fd90fab791b 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -14,13 +14,12 @@ %br - if @target_url - if @reply_by_email - Reply to this email directly or - #{link_to "view it on GitLab", @target_url}. + = _('Reply to this email directly or %{view_it_on_gitlab}.') % { view_it_on_gitlab: link_to(_("view it on GitLab"), @target_url) } - else - #{link_to "View it on GitLab", @target_url}. + #{link_to _("View it on GitLab"), @target_url}. %br -# Don't link the host in the line below, one link in the email is easier to quickly click than two. - You're receiving this email because #{notification_reason_text(@reason)}. + = _("You're receiving this email because %{reason}.") % { reason: notification_reason_text(@reason) } If you'd like to receive fewer emails, you can - if @labels_url adjust your #{link_to 'label subscriptions', @labels_url}. diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index 67aa05b655c003694e9fe35394588a37b7c9aa47..7aca64663e0c1522f953aaee8e8fdfaf52518968 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -1,5 +1,5 @@ -- page_title "User Settings" -- header_title "User Settings", profile_path unless header_title +- page_title _("User Settings") +- header_title _("User Settings"), profile_path unless header_title - sidebar "dashboard" - nav "profile" - @left_sidebar = true diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml index 4bc94bd132daa3cc911c961a5c000163e3f28559..93214c2a674bb923da33d38a9fa726197818508c 100644 --- a/app/views/layouts/project_settings.html.haml +++ b/app/views/layouts/project_settings.html.haml @@ -1,4 +1,4 @@ -- page_title "Settings" +- page_title _("Settings") - nav "project" = render template: "layouts/project" diff --git a/app/views/layouts/search.html.haml b/app/views/layouts/search.html.haml index fd4c7ad21a71501bc03cf01dffb42e56ce38305d..dd4b9e452078aa523644341573a7b4eeac9faadc 100644 --- a/app/views/layouts/search.html.haml +++ b/app/views/layouts/search.html.haml @@ -1,4 +1,4 @@ -- page_title "Search" -- header_title "Search", search_path +- page_title _("Search") +- header_title _("Search"), search_path = render template: "layouts/application" diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml index 849075a0ba5113d8691725bbc80418d85e0e8ff8..6418500d5d1666fa84b2263bd11f3c72f5052889 100644 --- a/app/views/layouts/snippets.html.haml +++ b/app/views/layouts/snippets.html.haml @@ -1,4 +1,4 @@ -- header_title "Snippets", snippets_path +- header_title _("Snippets"), snippets_path - content_for :page_specific_javascripts do - if @snippet && current_user diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 507cd5dcc121482f33f4295f4ef9d2cc6a4045d5..a4835584b5010acf2e72b96130cc02bdd766694a 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -69,6 +69,12 @@ = f.text_field :location = f.text_field :organization = f.text_area :bio, rows: 4, maxlength: 250, help: 'Tell us about yourself in fewer than 250 characters.' + %hr + %h5 Private profile + - private_profile_label = capture do + Don't display activity-related personal information on your profile + = link_to icon('question-circle'), help_page_path('user/profile/index.md', anchor: 'private-profile') + = f.check_box :private_profile, label: private_profile_label .prepend-top-default.append-bottom-default = f.submit 'Update profile settings', class: 'btn btn-success' = link_to 'Cancel', user_path(current_user), class: 'btn btn-cancel' diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index e90916e340d78a22107183cf2a3ea7038309670f..ef6f5c76de6d4f9dfb76f0c1bf06245f33e902b8 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -18,7 +18,7 @@ - commit = blame_group[:commit] %td.blame-commit{ class: age_map_class(commit.committed_date, project_duration) } .commit - = author_avatar(commit, size: 36) + = author_avatar(commit, size: 36, has_tooltip: false) .commit-row-title %span.item-title.str-truncated-100 = link_to_markdown commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index efb8175398b4f123bc40bd96f85f27ea78ca7644..5edab38bd6403db5aa09395a817caaac7475e8a4 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -3,6 +3,8 @@ - page_title @blob.path, @ref +.js-signature-container{ data: { 'signatures-path': namespace_project_signatures_path } } + %div{ class: container_class } = render 'projects/last_push' diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index d8b4266143e3236653e7eb6432b3d72607548958..a2dc2730ecc6e5d60898e0235f074def9bf3249e 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -1,5 +1,5 @@ - if current_user - %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } } + %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }> - if current_user.starred?(@project) = sprite_icon('star') %span.starred= _('Unstar') diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 886dd73c33b8d18654d088bebfdf46e71165615d..78522393d4b8049b44301e3ad5fb8bf6a8bded6b 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -10,7 +10,7 @@ %span.d-none.d-sm-inline authored #{time_ago_with_tooltip(@commit.authored_date)} %span= s_('ByAuthor|by') - = author_avatar(@commit, size: 24) + = author_avatar(@commit, size: 24, has_tooltip: false) %strong = commit_author_link(@commit, avatar: true, size: 24) - if @commit.different_committer? diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 90e55fd0fb017313c61b713c418c43b6fa41ca68..feaf44e8c0a5f0cfec4d7b8ba5070e0f37f9ee4b 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -19,7 +19,7 @@ %li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" } .avatar-cell.d-none.d-sm-block - = author_avatar(commit, size: 36) + = author_avatar(commit, size: 36, has_tooltip: false) .commit-detail.flex-list .commit-content.qa-commit-content diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 04131a90a571a19569a7a15d6f41eb1daa083fbd..bc247460d28de7a61220c97467af9c8dad65d228 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -33,3 +33,5 @@ %span.js-details-content.hide = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full" = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard") + + = render_if_exists "projects/pipelines/info_extension", pipeline: @pipeline diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 951f80b378d3aec0442011b5d36794a82fbf5c5d..c63ff070f709dac0cd0e0e3d8ebb1f72e4dad76b 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -12,6 +12,7 @@ = link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do = _("Failed Jobs") %span.badge.badge-pill.js-failures-counter= @pipeline.failed_builds.count + = render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project .tab-content #js-tab-pipeline.tab-pane @@ -75,4 +76,4 @@ %pre.build-trace.build-trace-rounded %code.bash.js-build-output = build_summary(build) - + = render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml index 956f8fef6b86b228af33cbe540b96faa1f00fd82..c13e3194340711882d9d3ca64e63b0296d07d00c 100644 --- a/app/views/projects/pipelines/new.html.haml +++ b/app/views/projects/pipelines/new.html.haml @@ -10,7 +10,7 @@ = form_errors(@pipeline) .form-group.row .col-sm-12 - = f.label :ref, s_('Pipeline|Create for') + = f.label :ref, s_('Pipeline|Create for'), class: 'col-form-label' = hidden_field_tag 'pipeline[ref]', params[:ref] || @project.default_branch = dropdown_tag(params[:ref] || @project.default_branch, options: { toggle_class: 'js-branch-select wide git-revision-dropdown-toggle', diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml index 9741b783db33b523ab51c97b829ef139e5180a8c..1d0b0265bb7319ba2670426092881ab195a36205 100644 --- a/app/views/projects/services/prometheus/_show.html.haml +++ b/app/views/projects/services/prometheus/_show.html.haml @@ -7,4 +7,4 @@ = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer" .col-lg-9 - = render_if_exists 'projects/services/prometheus/metrics', project: @project + = render 'projects/services/prometheus/metrics', project: @project diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index e28accd5b434c9caa7fb617718293a79d8043546..803ecca48f703bef2c644422c50b4cf28f0d3bc7 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -8,6 +8,10 @@ = render partial: 'flash_messages', locals: { project: @project } +- if @project.repository_exists? && !@project.empty_repo? + - signatures_path = namespace_project_signatures_path(project_id: @project.path, id: @project.default_branch) + .js-signature-container{ data: { 'signatures-path': signatures_path } } + %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } = render "projects/last_push" diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index 3b4057e56d01b77a1a6a87b0cc1dde5409532234..ace8120eefffa0f6ec25786a725dbaacc0687e09 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -1,11 +1,14 @@ - @no_container = true - breadcrumb_title _("Repository") - @content_class = "limit-container-width" unless fluid_layout +- signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.path, project_id: @project.path, id: @ref) - page_title @path.presence || _("Files"), @ref = content_for :meta_tags do = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") +.js-signature-container{ data: { 'signatures-path': signatures_path } } + %div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] } = render 'projects/last_push' = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id) diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml index e0832fd9136603ebebbd9a2fd002ce0d737b0dd3..9dd1c24fdfa6c3033da36eb153283397af9b105b 100644 --- a/app/views/shared/notes/_notes_with_form.html.haml +++ b/app/views/shared/notes/_notes_with_form.html.haml @@ -13,7 +13,7 @@ .flash-container.timeline-content .timeline-icon.d-none.d-sm-none.d-md-block - %a.author_link{ href: user_path(current_user) } + %a.author-link{ href: user_path(current_user) } = image_tag avatar_icon_for_user(current_user), alt: current_user.to_reference, class: 'avatar s40' .timeline-content.timeline-content-form = render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml index 37747faed62dfee23dc2ba1f805e2c6851e1900e..ddc089b0bd7c9100104c0ad043c3036476a69274 100644 --- a/app/views/sherlock/queries/_general.html.haml +++ b/app/views/sherlock/queries/_general.html.haml @@ -27,7 +27,7 @@ .card-header .float-right %button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button } - %i.fa.fa-clipboard + = sprite_icon('duplicate') %pre.hidden = @query.formatted_query %strong @@ -42,7 +42,7 @@ .card-header .float-right %button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button } - %i.fa.fa-clipboard + = sprite_icon('duplicate') %pre.hidden = @query.explain %strong diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index b2ec7166832c8cdc369897e1a650656fd42b9187..8d9e86d02c40974899915aff048fbedbc0185d2d 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -23,8 +23,9 @@ = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn', title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = icon('exclamation-circle') - = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do - = icon('rss') + - if can?(current_user, :read_user_profile, @user) + = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do + = icon('rss') - if current_user && current_user.admin? = link_to [:admin, @user], class: 'btn btn-default', title: 'View user in admin area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do @@ -40,10 +41,12 @@ = @user.name .cover-desc.member-date - %span.middle-dot-divider - @#{@user.username} - %span.middle-dot-divider - Member since #{@user.created_at.to_date.to_s(:long)} + %p + %span.middle-dot-divider + @#{@user.username} + - if can?(current_user, :read_user_profile, @user) + %span.middle-dot-divider + Member since #{@user.created_at.to_date.to_s(:long)} .cover-desc - unless @user.public_email.blank? @@ -78,30 +81,31 @@ %p.profile-user-bio = @user.bio - .scrolling-tabs-container - .fade-left= icon('angle-left') - .fade-right= icon('angle-right') - %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs - - if profile_tab?(:activity) - %li.js-activity-tab - = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do - Activity - - if profile_tab?(:groups) - %li.js-groups-tab - = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do - Groups - - if profile_tab?(:contributed) - %li.js-contributed-tab - = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do - Contributed projects - - if profile_tab?(:projects) - %li.js-projects-tab - = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do - Personal projects - - if profile_tab?(:snippets) - %li.js-snippets-tab - = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do - Snippets + - unless profile_tabs.empty? + .scrolling-tabs-container + .fade-left= icon('angle-left') + .fade-right= icon('angle-right') + %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs + - if profile_tab?(:activity) + %li.js-activity-tab + = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do + Activity + - if profile_tab?(:groups) + %li.js-groups-tab + = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do + Groups + - if profile_tab?(:contributed) + %li.js-contributed-tab + = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do + Contributed projects + - if profile_tab?(:projects) + %li.js-projects-tab + = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do + Personal projects + - if profile_tab?(:snippets) + %li.js-snippets-tab + = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do + Snippets %div{ class: container_class } .tab-content @@ -137,3 +141,13 @@ .loading-status = spinner + + - if profile_tabs.empty? + .row + .col-12 + .svg-content + = image_tag 'illustrations/profile_private_mode.svg' + .col-12.text-center + .text-content + %h4 + This user has a private profile diff --git a/app/workers/concerns/each_shard_worker.rb b/app/workers/concerns/each_shard_worker.rb index d0a728fb495f84c988f8498eaada715b73633669..00f589f957eddd988b8ff1b4d2c97e8559557e38 100644 --- a/app/workers/concerns/each_shard_worker.rb +++ b/app/workers/concerns/each_shard_worker.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module EachShardWorker extend ActiveSupport::Concern include ::Gitlab::Utils::StrongMemoize diff --git a/app/workers/delete_diff_files_worker.rb b/app/workers/delete_diff_files_worker.rb index bb8fbb9c3737238b034fee2235435f1a76ca5527..0874a0b75e884ded97bded5f081e7f3638e252a4 100644 --- a/app/workers/delete_diff_files_worker.rb +++ b/app/workers/delete_diff_files_worker.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeleteDiffFilesWorker include ApplicationWorker diff --git a/app/workers/repository_check/dispatch_worker.rb b/app/workers/repository_check/dispatch_worker.rb index 96634f09a15a15694286d2c0efdaa3aa9a2b34da..0a7d9a14c6a5433d35ceaf3f93f0070224f95708 100644 --- a/app/workers/repository_check/dispatch_worker.rb +++ b/app/workers/repository_check/dispatch_worker.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module RepositoryCheck class DispatchWorker include ApplicationWorker diff --git a/changelogs/unreleased/29278-commits-page-tooltips.yml b/changelogs/unreleased/29278-commits-page-tooltips.yml new file mode 100644 index 0000000000000000000000000000000000000000..d54301a1cf0c5706b20c5c8b413cfe979e9d881f --- /dev/null +++ b/changelogs/unreleased/29278-commits-page-tooltips.yml @@ -0,0 +1,5 @@ +--- +title: Remove tooltips from commit author avatar and name in commit lists +merge_request: 20674 +author: +type: other diff --git a/changelogs/unreleased/38604-add-private-profile.yml b/changelogs/unreleased/38604-add-private-profile.yml new file mode 100644 index 0000000000000000000000000000000000000000..e40e7d9321e8cbd63e78e648eb8dae4ea6165691 --- /dev/null +++ b/changelogs/unreleased/38604-add-private-profile.yml @@ -0,0 +1,5 @@ +--- +title: Add an option to have a private profile on GitLab. +merge_request: 20387 +author: jxterry +type: added diff --git a/changelogs/unreleased/40973-disable-rack-attack-by-default.yml b/changelogs/unreleased/40973-disable-rack-attack-by-default.yml new file mode 100644 index 0000000000000000000000000000000000000000..681aa761e2a5d44492cbdbaa1cec0f71f1303596 --- /dev/null +++ b/changelogs/unreleased/40973-disable-rack-attack-by-default.yml @@ -0,0 +1,5 @@ +--- +title: Rack attack is now disabled by default +merge_request: 16669 +author: +type: changed diff --git a/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml b/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml new file mode 100644 index 0000000000000000000000000000000000000000..b60aeba860a154a11a8fdb2fc32fdb0121e0abc7 --- /dev/null +++ b/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml @@ -0,0 +1,5 @@ +--- +title: Fix Vue datatype errors for markdownVersion parsing +merge_request: 20800 +author: +type: fixed diff --git a/changelogs/unreleased/45318-vuex-store.yml b/changelogs/unreleased/45318-vuex-store.yml new file mode 100644 index 0000000000000000000000000000000000000000..5ea89034bce06a0185ac6a58c4a75fa5a17dfed5 --- /dev/null +++ b/changelogs/unreleased/45318-vuex-store.yml @@ -0,0 +1,5 @@ +--- +title: Adds Vuex store for reports section in MR widget +merge_request: 20709 +author: +type: added diff --git a/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml b/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml new file mode 100644 index 0000000000000000000000000000000000000000..d490df581445489460242eaa29b9eb81b6be2443 --- /dev/null +++ b/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml @@ -0,0 +1,5 @@ +--- +title: Allow cloning LFS repositories through DeployTokens +merge_request: 20729 +author: +type: other diff --git a/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml b/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml deleted file mode 100644 index 7552e0d38781b3ea3d79ec3d76fe36cde4971a6b..0000000000000000000000000000000000000000 --- a/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add uploader support to Import/Export uploads -merge_request: 20484 -author: -type: added diff --git a/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml b/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml new file mode 100644 index 0000000000000000000000000000000000000000..0466debea65d07e4840f5a0acf4995c30eed5eb7 --- /dev/null +++ b/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml @@ -0,0 +1,5 @@ +--- +title: Disable SAML and Bitbucket if OmniAuth is disabled +merge_request: 20608 +author: +type: fixed diff --git a/changelogs/unreleased/49025-docs-kubernetes-tiller.yml b/changelogs/unreleased/49025-docs-kubernetes-tiller.yml new file mode 100644 index 0000000000000000000000000000000000000000..c4f01490cfa21cc2c5e058668bed7ca8057347b3 --- /dev/null +++ b/changelogs/unreleased/49025-docs-kubernetes-tiller.yml @@ -0,0 +1,5 @@ +--- +title: Update docs of Helm Tiller +merge_request: 20515 +author: Takuya Noguchi +type: other diff --git a/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml b/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml new file mode 100644 index 0000000000000000000000000000000000000000..c757e55f1cd43a9a9e29392c1ff54f2ef49f523b --- /dev/null +++ b/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml @@ -0,0 +1,5 @@ +--- +title: Sanitize git URL in import errors +merge_request: +author: Jamie Schembri +type: fixed diff --git a/changelogs/unreleased/api-minimal-access-level.yml b/changelogs/unreleased/api-minimal-access-level.yml new file mode 100644 index 0000000000000000000000000000000000000000..43cab246d6974ced9ff06fcfe5085f9ca793489a --- /dev/null +++ b/changelogs/unreleased/api-minimal-access-level.yml @@ -0,0 +1,5 @@ +--- +title: Add filter for minimal access level in groups and projects API +merge_request: 20478 +author: Marko, Peter +type: added diff --git a/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml new file mode 100644 index 0000000000000000000000000000000000000000..69e6b7d815a292c83df5f23fd8a89334ff1916e1 --- /dev/null +++ b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml @@ -0,0 +1,5 @@ +--- +title: Replace 'Sidekiq::Testing.inline!' with 'perform_enqueued_jobs' +merge_request: 20768 +author: "@blackst0ne" +type: other diff --git a/changelogs/unreleased/fix-filename-for-direct-uploads.yml b/changelogs/unreleased/fix-filename-for-direct-uploads.yml deleted file mode 100644 index a1bbf3704c0c7f26f6633ba5f7a7dc56ab801d30..0000000000000000000000000000000000000000 --- a/changelogs/unreleased/fix-filename-for-direct-uploads.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix filename for accelerated uploads -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/fj-48123-fix-gitlab-import.yml b/changelogs/unreleased/fj-48123-fix-gitlab-import.yml new file mode 100644 index 0000000000000000000000000000000000000000..896db2cdcb884ad60f0ff6e586a9ebaffecf1341 --- /dev/null +++ b/changelogs/unreleased/fj-48123-fix-gitlab-import.yml @@ -0,0 +1,5 @@ +--- +title: Fix GitLab project imports not loading due to API timeouts +merge_request: 20599 +author: +type: fixed diff --git a/changelogs/unreleased/frozen-string-danger.yml b/changelogs/unreleased/frozen-string-danger.yml new file mode 100644 index 0000000000000000000000000000000000000000..9910139b8a95072bd9057536901bad3309ed4d99 --- /dev/null +++ b/changelogs/unreleased/frozen-string-danger.yml @@ -0,0 +1,5 @@ +--- +title: Add Dangerfile for frozen_string_literal +merge_request: 20767 +author: gfyoung +type: performance diff --git a/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml b/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml new file mode 100644 index 0000000000000000000000000000000000000000..5c6b1b1a90410bc126d3b8d6c5a9c3635f9250b0 --- /dev/null +++ b/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen string in app/presenters and app/policies +merge_request: 20819 +author: gfyoung +type: performance diff --git a/changelogs/unreleased/frozen-string-enable-app-serializers.yml b/changelogs/unreleased/frozen-string-enable-app-serializers.yml new file mode 100644 index 0000000000000000000000000000000000000000..40c7b695d3974b0bcddb240e6e0561d0b055e2a0 --- /dev/null +++ b/changelogs/unreleased/frozen-string-enable-app-serializers.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen string in app/serializers/**/*.rb +merge_request: 20726 +author: gfyoung +type: performance diff --git a/changelogs/unreleased/frozen-string-vestigial.yml b/changelogs/unreleased/frozen-string-vestigial.yml new file mode 100644 index 0000000000000000000000000000000000000000..79e92a19a7a8ac11d60e33a0b1a98767995adb77 --- /dev/null +++ b/changelogs/unreleased/frozen-string-vestigial.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen string in newly added files to previously processed directories +merge_request: 20763 +author: gfyoung +type: performance diff --git a/changelogs/unreleased/gitaly-ff-branch-nil.yml b/changelogs/unreleased/gitaly-ff-branch-nil.yml deleted file mode 100644 index e7e689e605394bd4d89ea4dd804464d5a9706fb6..0000000000000000000000000000000000000000 --- a/changelogs/unreleased/gitaly-ff-branch-nil.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add missing Gitaly branch_update nil checks -merge_request: 20711 -author: -type: fixed diff --git a/changelogs/unreleased/hangouts_chat_integration.yml b/changelogs/unreleased/hangouts_chat_integration.yml new file mode 100644 index 0000000000000000000000000000000000000000..bf3484a6d0202e90a7f6cf212848ae1a028c3f75 --- /dev/null +++ b/changelogs/unreleased/hangouts_chat_integration.yml @@ -0,0 +1,5 @@ +--- +title: Add Hangouts Chat integration +merge_request: 20290 +author: Kukovskii Vladimir +type: added diff --git a/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml new file mode 100644 index 0000000000000000000000000000000000000000..a2fca4c5b91cabc0cee456219fca0c3b6d5eb0da --- /dev/null +++ b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml @@ -0,0 +1,5 @@ +--- +title: Show decimal place up to single digit in Stacked Progress Bar +merge_request: 20776 +author: +type: changed diff --git a/changelogs/unreleased/project-visibility-tooltip.yml b/changelogs/unreleased/project-visibility-tooltip.yml new file mode 100644 index 0000000000000000000000000000000000000000..806c93e493a6d7309e5ecaa150f2bb9e2b024bba --- /dev/null +++ b/changelogs/unreleased/project-visibility-tooltip.yml @@ -0,0 +1,5 @@ +--- +title: Fix project visibility tooltip +merge_request: 20535 +author: Jamie Schembri +type: fixed diff --git a/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml b/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml new file mode 100644 index 0000000000000000000000000000000000000000..8d5ecdfa57eb6c8d0d08577697a5c4a3080922c9 --- /dev/null +++ b/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml @@ -0,0 +1,5 @@ +--- +title: Replace snake case in SCSS variables +merge_request: 20799 +author: George Tsiolis +type: other diff --git a/changelogs/unreleased/replace-snake-case-css-classes.yml b/changelogs/unreleased/replace-snake-case-css-classes.yml new file mode 100644 index 0000000000000000000000000000000000000000..28ec5ee097fc2f82a0aad442cb4f3d05c211a833 --- /dev/null +++ b/changelogs/unreleased/replace-snake-case-css-classes.yml @@ -0,0 +1,5 @@ +--- +title: Replace author_link snake case in stylesheets, specs, and helpers +merge_request: 20797 +author: George Tsiolis +type: other diff --git a/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml new file mode 100644 index 0000000000000000000000000000000000000000..b9444440cb9d5da9e0fd935719d74f96908b2e83 --- /dev/null +++ b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml @@ -0,0 +1,5 @@ +--- +title: Bump nokogiri to 1.8.4 and sanitize to 4.6.6 for performance +merge_request: 20795 +author: +type: performance diff --git a/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml new file mode 100644 index 0000000000000000000000000000000000000000..897d673e97d59086aeac71396cfcdc3e71e66f63 --- /dev/null +++ b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen strings in remaining lib/banzai/filter/*.rb files +merge_request: 20777 +author: +type: performance diff --git a/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml new file mode 100644 index 0000000000000000000000000000000000000000..37b397ea49f1291cfec599b65581c338c028ab9a --- /dev/null +++ b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen strings in lib/banzai/filter/*.rb +merge_request: 20775 +author: +type: performance diff --git a/changelogs/unreleased/sh-remove-banzai-instrumentation.yml b/changelogs/unreleased/sh-remove-banzai-instrumentation.yml new file mode 100644 index 0000000000000000000000000000000000000000..8bb3cd5942b0b0c6e3265c508c620c8bea7f4daf --- /dev/null +++ b/changelogs/unreleased/sh-remove-banzai-instrumentation.yml @@ -0,0 +1,5 @@ +--- +title: Remove method instrumentation for Banzai filters and reference parsers +merge_request: 20770 +author: +type: performance diff --git a/changelogs/unreleased/sh-revert-markdown-changes.yml b/changelogs/unreleased/sh-revert-markdown-changes.yml new file mode 100644 index 0000000000000000000000000000000000000000..72540f710a1235176eb44986d0063dd9593e194b --- /dev/null +++ b/changelogs/unreleased/sh-revert-markdown-changes.yml @@ -0,0 +1,5 @@ +--- +title: Fix slow Markdown rendering +merge_request: 20820 +author: +type: performance diff --git a/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml b/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml new file mode 100644 index 0000000000000000000000000000000000000000..e8c2e11ad31c61a9840dc6b7028fd1d63c1c76ef --- /dev/null +++ b/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml @@ -0,0 +1,5 @@ +--- +title: Use limit parameter to retrieve Wikis from Gitaly +merge_request: 20764 +author: +type: performance diff --git a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml new file mode 100644 index 0000000000000000000000000000000000000000..16003fa9cadad539ce3d0a1b12139483a0d5b0f6 --- /dev/null +++ b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml @@ -0,0 +1,5 @@ +--- +title: Reduces the client side memory footprint on merge requests +merge_request: 20744 +author: +type: performance diff --git a/changelogs/unreleased/winh-tree-view-gpg.yml b/changelogs/unreleased/winh-tree-view-gpg.yml new file mode 100644 index 0000000000000000000000000000000000000000..84d63814a47c02f5173ecf2cf70675ac3d3c9008 --- /dev/null +++ b/changelogs/unreleased/winh-tree-view-gpg.yml @@ -0,0 +1,5 @@ +--- +title: Display GPG status on repository and blob pages +merge_request: 20524 +author: +type: changed diff --git a/changelogs/unreleased/zj-backup-timeout.yml b/changelogs/unreleased/zj-backup-timeout.yml new file mode 100644 index 0000000000000000000000000000000000000000..b2ad2ed8c63e4efefff8fde9627e853605aa79bd --- /dev/null +++ b/changelogs/unreleased/zj-backup-timeout.yml @@ -0,0 +1,5 @@ +--- +title: Disable Gitaly timeouts when creating or restoring backups +merge_request: 20810 +author: +type: fixed diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index ab109f5d04fcb0d6cbc23b5701ffac37c5ea2aaa..561ff57c9fb652243234e7cc146175b07d5759d0 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -78,15 +78,15 @@ production: &base # username_changing_enabled: false # default: true - User can change her username/namespace ## Default theme ID ## 1 - Indigo - ## 2 - Light Indigo - ## 3 - Blue - ## 4 - Light Blue + ## 2 - Dark + ## 3 - Light + ## 4 - Blue ## 5 - Green - ## 6 - Light Green - ## 7 - Red - ## 8 - Light Red - ## 9 - Dark - ## 10 - Light + ## 6 - Light Indigo + ## 7 - Light Blue + ## 8 - Light Green + ## 9 - Red + ## 10 - Light Red # default_theme: 1 # default: 1 ## Automatic issue closing diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index c3122827a6bc5c93827863ea1c0806cc13a363ce..9ad55e21d11c9ff7c5521d9a1c93ac28e1d8a5ab 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -1,4 +1,5 @@ require_relative '../settings' +require_relative '../object_store_settings' # Default settings Settings['ldap'] ||= Settingslogic.new({}) @@ -179,14 +180,7 @@ Settings.artifacts['storage_path'] = Settings.absolute(Settings.artifacts.values # Settings.artifact['path'] is deprecated, use `storage_path` instead Settings.artifacts['path'] = Settings.artifacts['storage_path'] Settings.artifacts['max_size'] ||= 100 # in megabytes -Settings.artifacts['object_store'] ||= Settingslogic.new({}) -Settings.artifacts['object_store']['enabled'] = false if Settings.artifacts['object_store']['enabled'].nil? -Settings.artifacts['object_store']['remote_directory'] ||= nil -Settings.artifacts['object_store']['direct_upload'] = false if Settings.artifacts['object_store']['direct_upload'].nil? -Settings.artifacts['object_store']['background_upload'] = true if Settings.artifacts['object_store']['background_upload'].nil? -Settings.artifacts['object_store']['proxy_download'] = false if Settings.artifacts['object_store']['proxy_download'].nil? -# Convert upload connection settings to use string keys, to make Fog happy -Settings.artifacts['object_store']['connection']&.deep_stringify_keys! +Settings.artifacts['object_store'] = ObjectStoreSettings.parse(Settings.artifacts['object_store']) # # Registry @@ -225,14 +219,7 @@ Settings.pages.admin['certificate'] ||= '' Settings['lfs'] ||= Settingslogic.new({}) Settings.lfs['enabled'] = true if Settings.lfs['enabled'].nil? Settings.lfs['storage_path'] = Settings.absolute(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects")) -Settings.lfs['object_store'] ||= Settingslogic.new({}) -Settings.lfs['object_store']['enabled'] = false if Settings.lfs['object_store']['enabled'].nil? -Settings.lfs['object_store']['remote_directory'] ||= nil -Settings.lfs['object_store']['direct_upload'] = false if Settings.lfs['object_store']['direct_upload'].nil? -Settings.lfs['object_store']['background_upload'] = true if Settings.lfs['object_store']['background_upload'].nil? -Settings.lfs['object_store']['proxy_download'] = false if Settings.lfs['object_store']['proxy_download'].nil? -# Convert upload connection settings to use string keys, to make Fog happy -Settings.lfs['object_store']['connection']&.deep_stringify_keys! +Settings.lfs['object_store'] = ObjectStoreSettings.parse(Settings.lfs['object_store']) # # Uploads @@ -240,14 +227,8 @@ Settings.lfs['object_store']['connection']&.deep_stringify_keys! Settings['uploads'] ||= Settingslogic.new({}) Settings.uploads['storage_path'] = Settings.absolute(Settings.uploads['storage_path'] || 'public') Settings.uploads['base_dir'] = Settings.uploads['base_dir'] || 'uploads/-/system' -Settings.uploads['object_store'] ||= Settingslogic.new({}) -Settings.uploads['object_store']['enabled'] = false if Settings.uploads['object_store']['enabled'].nil? +Settings.uploads['object_store'] = ObjectStoreSettings.parse(Settings.uploads['object_store']) Settings.uploads['object_store']['remote_directory'] ||= 'uploads' -Settings.uploads['object_store']['direct_upload'] = false if Settings.uploads['object_store']['direct_upload'].nil? -Settings.uploads['object_store']['background_upload'] = true if Settings.uploads['object_store']['background_upload'].nil? -Settings.uploads['object_store']['proxy_download'] = false if Settings.uploads['object_store']['proxy_download'].nil? -# Convert upload connection settings to use string keys, to make Fog happy -Settings.uploads['object_store']['connection']&.deep_stringify_keys! # # Mattermost @@ -437,7 +418,7 @@ Settings['extra'] ||= Settingslogic.new({}) # Settings['rack_attack'] ||= Settingslogic.new({}) Settings.rack_attack['git_basic_auth'] ||= Settingslogic.new({}) -Settings.rack_attack.git_basic_auth['enabled'] = true if Settings.rack_attack.git_basic_auth['enabled'].nil? +Settings.rack_attack.git_basic_auth['enabled'] = false if Settings.rack_attack.git_basic_auth['enabled'].nil? Settings.rack_attack.git_basic_auth['ip_whitelist'] ||= %w{127.0.0.1} Settings.rack_attack.git_basic_auth['maxretry'] ||= 10 Settings.rack_attack.git_basic_auth['findtime'] ||= 1.minute diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb index 8a851b89c56f1e783d3c7f70df57c86dde2dde7e..4d8d35bf6cf402135af605c23b3d15ca5f54bfcc 100644 --- a/config/initializers/8_metrics.rb +++ b/config/initializers/8_metrics.rb @@ -58,20 +58,6 @@ def instrument_classes(instrumentation) instrumentation.instrument_instance_methods(const) end - # Instruments all Banzai filters and reference parsers - { - Filter: Rails.root.join('lib', 'banzai', 'filter', '*.rb'), - ReferenceParser: Rails.root.join('lib', 'banzai', 'reference_parser', '*.rb') - }.each do |const_name, path| - Dir[path].each do |file| - klass = File.basename(file, File.extname(file)).camelize - const = Banzai.const_get(const_name).const_get(klass) - - instrumentation.instrument_methods(const) - instrumentation.instrument_instance_methods(const) - end - end - instrumentation.instrument_methods(Banzai::Renderer) instrumentation.instrument_methods(Banzai::Querying) @@ -94,8 +80,6 @@ def instrument_classes(instrumentation) instrumentation.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker) - instrumentation.instrument_instance_methods(Rouge::Plugins::CommonMark) - instrumentation.instrument_instance_methods(Rouge::Plugins::Redcarpet) instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab) [:XML, :HTML].each do |namespace| diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index e5772c3330797f4cb7478b573bdf46dfee871044..c41b2db722c6b18f368e6ce927cfa9365b015daf 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -219,7 +219,7 @@ Devise.setup do |config| end end - if Gitlab::OmniauthInitializer.enabled? + if Gitlab::Auth.omniauth_enabled? Gitlab::OmniauthInitializer.new(config).execute(Gitlab.config.omniauth.providers) end end diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index c558eb28ced45a95a7f6c7f56aac4fdf8ffcadf5..ef23ca065c612340f43f46d9e69461ba25cdf682 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -16,8 +16,3 @@ OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_s OmniAuth.config.before_request_phase do |env| Gitlab::RequestForgeryProtection.call(env) end - -if Gitlab::OmniauthInitializer.enabled? - provider_names = Gitlab.config.omniauth.providers.map(&:name) - Gitlab::Auth.omniauth_setup_providers(provider_names) -end diff --git a/config/object_store_settings.rb b/config/object_store_settings.rb new file mode 100644 index 0000000000000000000000000000000000000000..d85ff394dcc180ffb35671bc1e4865a5bde4f43b --- /dev/null +++ b/config/object_store_settings.rb @@ -0,0 +1,15 @@ +# Set default values for object_store settings +class ObjectStoreSettings + def self.parse(object_store) + object_store ||= Settingslogic.new({}) + object_store['enabled'] = false if object_store['enabled'].nil? + object_store['remote_directory'] ||= nil + object_store['direct_upload'] = false if object_store['direct_upload'].nil? + object_store['background_upload'] = true if object_store['background_upload'].nil? + object_store['proxy_download'] = false if object_store['proxy_download'].nil? + + # Convert upload connection settings to use string keys, to make Fog happy + object_store['connection']&.deep_stringify_keys! + object_store + end +end diff --git a/danger/frozen_string/Dangerfile b/danger/frozen_string/Dangerfile new file mode 100644 index 0000000000000000000000000000000000000000..595176d597d6e4819857f431f0ecf2a74a3513e5 --- /dev/null +++ b/danger/frozen_string/Dangerfile @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +FILE_EXTENSION = ".rb" +MAGIC_COMMENT = "# frozen_string_literal: true" + +def get_files_with_no_magic_comment(files) + files.select do |file| + file.end_with?(FILE_EXTENSION) && + !File.open(file, &:gets).start_with?(MAGIC_COMMENT) + end +end + +files_to_check = git.added_files +files_to_fix = get_files_with_no_magic_comment(files_to_check) + +if files_to_fix.any? + warn 'This merge request adds files that do not enforce frozen string literal. ' \ + 'See https://gitlab.com/gitlab-org/gitlab-ce/issues/47424 for more information.' + + markdown(<<~MARKDOWN) +## Enable Frozen String Literal + +The following files should have `#{MAGIC_COMMENT}` in the first line: + +* #{files_to_fix.map { |path| "`#{path}`" }.join("\n* ")} + MARKDOWN +end diff --git a/db/migrate/20180722103201_add_private_profile_to_users.rb b/db/migrate/20180722103201_add_private_profile_to_users.rb new file mode 100644 index 0000000000000000000000000000000000000000..4f7ef1322d8cf7bdd69a3cad7eb6a833320d7dfa --- /dev/null +++ b/db/migrate/20180722103201_add_private_profile_to_users.rb @@ -0,0 +1,10 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddPrivateProfileToUsers < ActiveRecord::Migration + DOWNTIME = false + + def change + add_column :users, :private_profile, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 1a5555fb3a4f5edc02d597be0984fd642d7265bf..3db11d8447ec9ee5b9ae3b3881fbf5d95af1b9b2 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: 20180704204006) do +ActiveRecord::Schema.define(version: 20180722103201) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -2124,6 +2124,7 @@ ActiveRecord::Schema.define(version: 20180704204006) do t.integer "theme_id", limit: 2 t.integer "accepted_term_id" t.string "feed_token" + t.boolean "private_profile" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md index 0d9c10687f2993bde150fcb0ce0a5665516ab6f1..637d44d2823fdb3420582cc445359d0ff0c2fea3 100644 --- a/doc/administration/high_availability/gitlab.md +++ b/doc/administration/high_availability/gitlab.md @@ -122,6 +122,11 @@ need some extra configuration. from running on upgrade. Only the primary GitLab application server should handle migrations. +1. **Optional** Configure host keys. Copy all contents(primary and public keys) inside `/etc/ssh/` on + the primary application server to `/etc/ssh` on all secondary servers. This + prevents false man-in-the-middle-attack alerts when accessing servers in your + High Availability cluster behind a load balancer. + 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. ## Troubleshooting diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index c0e98099e4262f993def6e7b3ba3ce1a55647533..7bc92ae77eefa3a6c67a1eb98ad449664aa0ccd4 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -22,7 +22,7 @@ collect metrics from this endpoint. We recommend setting up another Prometheus server, because the embedded server configuration is overwritten once every [reconfigure of GitLab][reconfigure]. In the future this will not be required. -## Metrics available +## Unicorn Metrics available The following metrics are available: diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index b3602bc35ab687986a25b1d1926f9616dad113f8..eefa86f8e42a74a7e5d10d0f2e9520a3f6b86c31 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -124,11 +124,6 @@ The Pages daemon doesn't listen to the outside world. ``` 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: - - ```shell - sudo gitlab-ctl restart gitlab-pages - ``` Watch the [video tutorial][video-admin] for this configuration. @@ -161,11 +156,6 @@ outside world. respectively. 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: - - ```shell - sudo gitlab-ctl restart gitlab-pages - ``` ## Advanced configuration @@ -203,11 +193,6 @@ world. Custom domains are supported, but no TLS. listens on. If you don't have IPv6, you can omit the IPv6 address. 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: - - ```shell - sudo gitlab-ctl restart gitlab-pages - ``` ### Custom domains with TLS support @@ -241,11 +226,6 @@ world. Custom domains and TLS are supported. listens on. If you don't have IPv6, you can omit the IPv6 address. 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: - - ```shell - sudo gitlab-ctl restart gitlab-pages - ``` ### Custom domain verification @@ -267,7 +247,6 @@ Omnibus GitLab 11.1. Follow the steps below to configure verbose logging of GitLab Pages daemon. 1. By default the daemon only logs with `INFO` level. - If you wish to make it log events with level `DEBUG` you must configure this in `/etc/gitlab/gitlab.rb`: @@ -291,11 +270,29 @@ are stored. ``` 1. [Reconfigure GitLab][reconfigure] -1. Restart gitlab-pages by running the following command: + +## Configure listener for reverse proxy requests + +Follow the steps below to configure the proxy listener of GitLab Pages. [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2533) in +Omnibus GitLab 11.1. + +1. By default the listener is configured to listen for requests on `localhost:8090`. + + If you wish to disable it you must configure this in + `/etc/gitlab/gitlab.rb`: ```shell - sudo gitlab-ctl restart gitlab-pages - ``` + gitlab_pages['listen_proxy'] = nil + ``` + + If you wish to make it listen on a different port you must configure this also in + `/etc/gitlab/gitlab.rb`: + + ```shell + gitlab_pages['listen_proxy'] = "localhost:10080" + ``` + +1. [Reconfigure GitLab][reconfigure] ## Set maximum pages size diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md index 2649bf61d74c94d30ad6b43f55f639ebe1edffd0..0c145830f027507db93d8939496a75e57b93a2b7 100644 --- a/doc/administration/raketasks/check.md +++ b/doc/administration/raketasks/check.md @@ -121,6 +121,45 @@ sudo gitlab-rake gitlab:lfs:check BATCH=100 ID_FROM=50 ID_TO=250 sudo gitlab-rake gitlab:uploads:check BATCH=100 ID_FROM=50 ID_TO=250 ``` +Example output: + +``` +$ sudo gitlab-rake gitlab:uploads:check +Checking integrity of Uploads +- 1..1350: Failures: 0 +- 1351..2743: Failures: 0 +- 2745..4349: Failures: 2 +- 4357..5762: Failures: 1 +- 5764..7140: Failures: 2 +- 7142..8651: Failures: 0 +- 8653..10134: Failures: 0 +- 10135..11773: Failures: 0 +- 11777..13315: Failures: 0 +Done! +``` + +Example verbose output: + +``` +$ sudo gitlab-rake gitlab:uploads:check VERBOSE=1 +Checking integrity of Uploads +- 1..1350: Failures: 0 +- 1351..2743: Failures: 0 +- 2745..4349: Failures: 2 + - Upload: 3573: #<Errno::ENOENT: No such file or directory @ rb_sysopen - /opt/gitlab/embedded/service/gitlab-rails/public/uploads/user-foo/project-bar/7a77cc52947bfe188adeff42f890bb77/image.png> + - Upload: 3580: #<Errno::ENOENT: No such file or directory @ rb_sysopen - /opt/gitlab/embedded/service/gitlab-rails/public/uploads/user-foo/project-bar/2840ba1ba3b2ecfa3478a7b161375f8a/pug.png> +- 4357..5762: Failures: 1 + - Upload: 4636: #<Google::Apis::ServerError: Server error> +- 5764..7140: Failures: 2 + - Upload: 5812: #<NoMethodError: undefined method `hashed_storage?' for nil:NilClass> + - Upload: 5837: #<NoMethodError: undefined method `hashed_storage?' for nil:NilClass> +- 7142..8651: Failures: 0 +- 8653..10134: Failures: 0 +- 10135..11773: Failures: 0 +- 11777..13315: Failures: 0 +Done! +``` + ## LDAP Check The LDAP check Rake task will test the bind_dn and password credentials diff --git a/doc/api/groups.md b/doc/api/groups.md index 11de75039eedbcf12e1fcff3ff73c830cc1b72b1..87be36cc81512ccb929fa93f43e10c04bb73f9ca 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -10,13 +10,14 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `skip_groups` | array of integers | no | Skip the group IDs passed | -| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) | +| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence | | `search` | string | no | Return the list of authorized groups matching the search criteria | | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `statistics` | boolean | no | Include group statistics (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | -| `owned` | boolean | no | Limit to groups owned by the current user | +| `owned` | boolean | no | Limit to groups explicitly owned by the current user | +| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) | ``` GET /groups @@ -94,13 +95,14 @@ Parameters: | --------- | ---- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group | | `skip_groups` | array of integers | no | Skip the group IDs passed | -| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) | +| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence | | `search` | string | no | Return the list of authorized groups matching the search criteria | | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `statistics` | boolean | no | Include group statistics (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | -| `owned` | boolean | no | Limit to groups owned by the current user | +| `owned` | boolean | no | Limit to groups explicitly owned by the current user | +| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) | ``` GET /groups/:id/subgroups diff --git a/doc/api/issues.md b/doc/api/issues.md index 5613cb6d9154482dfaea41dd6a2b3976681830c4..92fb3e9c307e39b90a1c0e608e1c5e9ca5d84673 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -37,7 +37,7 @@ GET /issues?my_reaction_emoji=star | ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `state` | string | no | Return all issues or just those that are `opened` or `closed` | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | -| `milestone` | string | no | The milestone title | +| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone | | `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ | | `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ | | `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | @@ -151,7 +151,7 @@ GET /groups/:id/issues?my_reaction_emoji=star | `state` | string | no | Return all issues or just those that are `opened` or `closed` | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `iids[]` | Array[integer] | no | Return only the issues having the given `iid` | -| `milestone` | string | no | The milestone title | +| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone | | `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ | | `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | | `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | @@ -265,7 +265,7 @@ GET /projects/:id/issues?my_reaction_emoji=star | `iids[]` | Array[integer] | no | Return only the milestone having the given `iid` | | `state` | string | no | Return all issues or just those that are `opened` or `closed` | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | -| `milestone` | string | no | The milestone title | +| `milestone` | string | no | The milestone title. `No+Milestone` lists all issues with no milestone | | `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ | | `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | | `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | diff --git a/doc/api/jobs.md b/doc/api/jobs.md index cfa5e9a3e95f369fb5cf538db7afb04d794f6515..9a950097675ea4cb836654e6584cb12f7281e6d7 100644 --- a/doc/api/jobs.md +++ b/doc/api/jobs.md @@ -50,6 +50,7 @@ Example of response "started_at": "2015-12-24T17:54:24.729Z", "status": "failed", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/6", "user": { "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "bio": null, @@ -82,7 +83,7 @@ Example of response "size": 1000 }, "finished_at": "2015-12-24T17:54:27.895Z", - "artifacts_expire_at": "2016-01-23T17:54:27.895Z" + "artifacts_expire_at": "2016-01-23T17:54:27.895Z", "id": 7, "name": "teaspoon", "pipeline": { @@ -97,6 +98,7 @@ Example of response "started_at": "2015-12-24T17:54:27.722Z", "status": "failed", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/7", "user": { "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "bio": null, @@ -151,7 +153,7 @@ Example of response "created_at": "2015-12-24T15:51:21.727Z", "artifacts_file": null, "finished_at": "2015-12-24T17:54:24.921Z", - "artifacts_expire_at": "2016-01-23T17:54:24.921Z" + "artifacts_expire_at": "2016-01-23T17:54:24.921Z", "id": 6, "name": "rspec:other", "pipeline": { @@ -166,6 +168,7 @@ Example of response "started_at": "2015-12-24T17:54:24.729Z", "status": "failed", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/6", "user": { "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "bio": null, @@ -198,7 +201,7 @@ Example of response "size": 1000 }, "finished_at": "2015-12-24T17:54:27.895Z", - "artifacts_expire_at": "2016-01-23T17:54:27.895Z" + "artifacts_expire_at": "2016-01-23T17:54:27.895Z", "id": 7, "name": "teaspoon", "pipeline": { @@ -213,6 +216,7 @@ Example of response "started_at": "2015-12-24T17:54:27.722Z", "status": "failed", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/7", "user": { "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "bio": null, @@ -280,6 +284,7 @@ Example of response "started_at": "2015-12-24T17:54:30.733Z", "status": "failed", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/8", "user": { "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "bio": null, @@ -455,7 +460,7 @@ Example of response "created_at": "2016-01-11T10:13:33.506Z", "artifacts_file": null, "finished_at": "2016-01-11T10:14:09.526Z", - "id": 69, + "id": 42, "name": "rubocop", "ref": "master", "runner": null, @@ -463,6 +468,7 @@ Example of response "started_at": null, "status": "canceled", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/42", "user": null } ``` @@ -501,7 +507,7 @@ Example of response "created_at": "2016-01-11T10:13:33.506Z", "artifacts_file": null, "finished_at": null, - "id": 69, + "id": 42, "name": "rubocop", "ref": "master", "runner": null, @@ -509,6 +515,7 @@ Example of response "started_at": null, "status": "pending", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/42", "user": null } ``` @@ -549,7 +556,7 @@ Example of response }, "coverage": null, "download_url": null, - "id": 69, + "id": 42, "name": "rubocop", "ref": "master", "runner": null, @@ -559,6 +566,7 @@ Example of response "finished_at": "2016-01-11T10:15:10.506Z", "status": "failed", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/42", "user": null } ``` @@ -599,7 +607,7 @@ Example response: }, "coverage": null, "download_url": null, - "id": 69, + "id": 42, "name": "rubocop", "ref": "master", "runner": null, @@ -609,6 +617,7 @@ Example response: "finished_at": "2016-01-11T10:15:10.506Z", "status": "failed", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/42", "user": null } ``` @@ -647,7 +656,7 @@ Example of response "created_at": "2016-01-11T10:13:33.506Z", "artifacts_file": null, "finished_at": null, - "id": 69, + "id": 42, "name": "rubocop", "ref": "master", "runner": null, @@ -655,6 +664,7 @@ Example of response "started_at": null, "status": "started", "tag": false, + "web_url": "https://example.com/foo/bar/-/jobs/42", "user": null } ``` diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md index 22cf9afbcd2f6ce36fde91c2d8e490a8ceaf153e..574be52801ce5e30b2c71ae46c1a338075baed43 100644 --- a/doc/api/pipelines.md +++ b/doc/api/pipelines.md @@ -33,13 +33,15 @@ Example of response "id": 47, "status": "pending", "ref": "new-pipeline", - "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a" + "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a", + "web_url": "https://example.com/foo/bar/pipelines/47" }, { "id": 48, "status": "pending", "ref": "new-pipeline", - "sha": "eb94b618fb5865b26e80fdd8ae531b7a63ad851a" + "sha": "eb94b618fb5865b26e80fdd8ae531b7a63ad851a", + "web_url": "https://example.com/foo/bar/pipelines/48" } ] ``` @@ -86,7 +88,8 @@ Example of response "finished_at": "2016-08-11T11:32:35.145Z", "committed_at": null, "duration": null, - "coverage": "30.0" + "coverage": "30.0", + "web_url": "https://example.com/foo/bar/pipelines/46" } ``` @@ -133,7 +136,8 @@ Example of response "finished_at": null, "committed_at": null, "duration": null, - "coverage": null + "coverage": null, + "web_url": "https://example.com/foo/bar/pipelines/61" } ``` @@ -179,7 +183,8 @@ Response: "finished_at": "2016-08-11T11:32:35.145Z", "committed_at": null, "duration": null, - "coverage": null + "coverage": null, + "web_url": "https://example.com/foo/bar/pipelines/46" } ``` @@ -225,7 +230,8 @@ Response: "finished_at": "2016-08-11T11:32:35.145Z", "committed_at": null, "duration": null, - "coverage": null + "coverage": null, + "web_url": "https://example.com/foo/bar/pipelines/46" } ``` diff --git a/doc/api/projects.md b/doc/api/projects.md index f3ccca46420b16c839fa2eef1f32d518a8b2deb1..f360b49c293e544ad9542c402ad0fd3e76b59b7f 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -48,7 +48,7 @@ GET /projects | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of projects matching the search criteria | | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. | -| `owned` | boolean | no | Limit by projects owned by the current user | +| `owned` | boolean | no | Limit by projects explicitly owned by the current user | | `membership` | boolean | no | Limit by projects that the current user is a member of | | `starred` | boolean | no | Limit by projects starred by the current user | | `statistics` | boolean | no | Include project statistics | @@ -57,6 +57,7 @@ GET /projects | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `wiki_checksum_failed` | boolean | no | Limit projects where the wiki checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ | | `repository_checksum_failed` | boolean | no | Limit projects where the repository checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ | +| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) | When `simple=true` or the user is unauthenticated this returns something like: @@ -273,13 +274,14 @@ GET /users/:user_id/projects | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of projects matching the search criteria | | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. | -| `owned` | boolean | no | Limit by projects owned by the current user | +| `owned` | boolean | no | Limit by projects explicitly owned by the current user | | `membership` | boolean | no | Limit by projects that the current user is a member of | | `starred` | boolean | no | Limit by projects starred by the current user | | `statistics` | boolean | no | Include project statistics | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | +| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) | ```json [ @@ -573,7 +575,15 @@ If the project is a fork, and you provide a valid token to authenticate, the "avatar_url":"https://assets.gitlab-static.net/uploads/-/system/project/avatar/13083/logo-extra-whitespace.png", "star_count":3812, "forks_count":3561, - "last_activity_at":"2018-01-02T11:40:26.570Z" + "last_activity_at":"2018-01-02T11:40:26.570Z", + "namespace": { + "id": 72, + "name": "GitLab.org", + "path": "gitlab-org", + "kind": "group", + "full_path": "gitlab-org", + "parent_id": null + } } ... @@ -761,13 +771,14 @@ GET /projects/:id/forks | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of projects matching the search criteria | | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. | -| `owned` | boolean | no | Limit by projects owned by the current user | +| `owned` | boolean | no | Limit by projects explicitly owned by the current user | | `membership` | boolean | no | Limit by projects that the current user is a member of | | `starred` | boolean | no | Limit by projects starred by the current user | | `statistics` | boolean | no | Include project statistics | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | +| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) | ```bash curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/forks" diff --git a/doc/api/services.md b/doc/api/services.md index aeb48ccc36c3c6c3984b1d8429619c14d9b48203..efa173180bb43ca01698081a73654be3bc58e1f8 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -443,6 +443,54 @@ Get Gemnasium service settings for a project. GET /projects/:id/services/gemnasium ``` +## Hangouts Chat + +Google GSuite team collaboration tool. + +>**Note:** This service was [introduced in v11.2](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20290) + +### Create/Edit Hangouts Chat service + +Set Hangouts Chat service for a project. + +``` +PUT /projects/:id/services/hangouts_chat +``` + +>**Note:** Specific event parameters (e.g. `push_events` flag) were [introduced in v10.4][11435] + +Parameters: + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `webhook` | string | true | The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces... | +| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines | +| `notify_only_default_branch` | boolean | false | Send notifications only for the default branch | +| `push_events` | boolean | false | Enable notifications for push events | +| `issues_events` | boolean | false | Enable notifications for issue events | +| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events | +| `merge_requests_events` | boolean | false | Enable notifications for merge request events | +| `tag_push_events` | boolean | false | Enable notifications for tag push events | +| `note_events` | boolean | false | Enable notifications for note events | +| `pipeline_events` | boolean | false | Enable notifications for pipeline events | +| `wiki_page_events` | boolean | false | Enable notifications for wiki page events | + +### Delete Hangouts Chat service + +Delete Hangouts Chat service for a project. + +``` +DELETE /projects/:id/services/hangouts_chat +``` + +### Get Hangouts Chat service settings + +Get Hangouts Chat service settings for a project. + +``` +GET /projects/:id/services/hangouts_chat +``` + ## HipChat Private group chat and IM diff --git a/doc/api/users.md b/doc/api/users.md index 72fdaaa2c74faa7efc300a137871ee122779ad1f..07f9baf06d295986e14ccc4443ff4e014318c300 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -105,7 +105,8 @@ GET /users "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false }, { "id": 2, @@ -135,7 +136,8 @@ GET /users "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false } ] ``` @@ -248,7 +250,8 @@ Parameters: "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false } ``` @@ -288,6 +291,7 @@ Parameters: - `skip_confirmation` (optional) - Skip confirmation - true or false (default) - `external` (optional) - Flags the user as external - true or false(default) - `avatar` (optional) - Image file for user's avatar +- `private_profile` (optional) - User's profile is private - true or false ## User modification @@ -318,6 +322,7 @@ Parameters: - `skip_reconfirmation` (optional) - Skip reconfirmation - true or false (default) - `external` (optional) - Flags the user as external - true or false(default) - `avatar` (optional) - Image file for user's avatar +- `private_profile` (optional) - User's profile is private - true or false On password update, user will be forced to change it upon next login. Note, at the moment this method does only return a `404` error, @@ -382,7 +387,8 @@ GET /user "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false } ``` @@ -429,7 +435,8 @@ GET /user "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false } ``` diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index fbac37e688e2c798bebe32e84ddc166355e71d83..63338ff632cbaa49206907e5381397a487106a13 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -427,7 +427,7 @@ services: variables: DOCKER_HOST: tcp://docker:2375 DOCKER_DRIVER: overlay2 - IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME + IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG before_script: - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY @@ -440,8 +440,10 @@ build: ``` Here, `$CI_REGISTRY_IMAGE` would be resolved to the address of the registry tied -to this project, and `$CI_COMMIT_REF_NAME` would be resolved to the branch or -tag name for this particular job. We also declare our own variable, `$IMAGE_TAG`, +to this project. Since `$CI_COMMIT_REF_NAME` resolves to the branch or tag name, +and your branch-name can contain forward slashes (e.g., feature/my-feature), it is +safer to use `$CI_COMMIT_REF_SLUG` as the image tag. This is due to that image tags +cannot contain forward slashes. We also declare our own variable, `$IMAGE_TAG`, combining the two to save us some typing in the `script` section. Here's a more elaborate example that splits up the tasks into 4 pipeline stages, @@ -464,7 +466,7 @@ stages: variables: DOCKER_HOST: tcp://docker:2375 DOCKER_DRIVER: overlay2 - CONTAINER_TEST_IMAGE: registry.example.com/my-group/my-project/my-image:$CI_COMMIT_REF_NAME + CONTAINER_TEST_IMAGE: registry.example.com/my-group/my-project/my-image:$CI_COMMIT_REF_SLUG CONTAINER_RELEASE_IMAGE: registry.example.com/my-group/my-project/my-image:latest before_script: diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 84bd64d50cd3dc244529a1606b2e90b0455e8052..115e6e390c956384bded9e396b9bb21c0ebe07f5 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -64,7 +64,7 @@ future GitLab releases.** | **CI_JOB_MANUAL** | 8.12 | all | The flag to indicate that job was manually started | | **CI_JOB_NAME** | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` | | **CI_JOB_STAGE** | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` | -| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the GitLab Container Registry | +| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the [GitLab Container Registry][registry] and downloading [dependent repositories][dependent-repositories] | | **CI_JOB_URL** | 11.1 | 0.5 | Job details URL | | **CI_REPOSITORY_URL** | 9.0 | all | The URL to clone the Git repository | | **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab | @@ -566,3 +566,5 @@ Below you can find supported syntax reference: [subgroups]: ../../user/group/subgroups/index.md [builds-policies]: ../yaml/README.md#only-and-except-complex [gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token +[registry]: ../../user/project/container_registry.md +[dependent-repositories]: ../../user/project/new_ci_build_permissions_model.md#dependent-repositories \ No newline at end of file diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 096b64eb8810a8103bc553571bf4f32764e6d486..d95f8c7c8ccb2c9fd86926597bd8e23771af8e35 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1411,43 +1411,6 @@ variables: You can set it globally or per-job in the [`variables`](#variables) section. -### Custom build directories - -> [Introduced][gitlab-runner-876] in Gitlab Runner 11.1 - -NOTE: **Note:** -This can only be used when `custom_build_dir` is set to true in the [Runner's -configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html). - -By default, GitLab Runner clones the repository in the `/builds` directory, -but sometimes your project might require to have the code in a specific -directory, like the GO projects for example. In that case, you can specify -the `CI_PROJECT_DIR` variable to tell the Runner in which directory to clone -the repository: - -```yml -image: golang:1.10-alpine3.7 - -variables: - CI_PROJECT_DIR: /go/src/gitlab.com/namespace/project-name - -stages: - - test - -dir: - stage: test - script: - - pwd # /go/src/gitlab.com/namespace/project-name -``` - -The following executors may use this feature only when -[concurrent](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section) -is set to `1`: - -- `shell` -- `ssh` -- `docker`, `docker+machine` when the job's working directory is mounted as a host volume. - ## Special YAML features It's possible to use special YAML features like anchors (`&`), aliases (`*`) @@ -1641,6 +1604,5 @@ CI with various languages. [ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983 [ce-7447]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7447 [ce-12909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12909 -[gitlab-runner-876]: https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/876 [schedules]: ../../user/project/pipelines/schedules.md [variables-expressions]: ../variables/README.md#variables-expressions diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md index 46c5baddb9cbc12e7873b93d1712628ae41e2408..16195cbbbdf119d2cb921ab22b20afe41014ac7e 100644 --- a/doc/development/background_migrations.md +++ b/doc/development/background_migrations.md @@ -296,10 +296,18 @@ for more details. ## Best practices +1. Make sure to know how much data you're dealing with 1. Make sure that background migration jobs are idempotent. 1. Make sure that tests you write are not false positives. 1. Make sure that if the data being migrated is critical and cannot be lost, the clean-up migration also checks the final state of the data before completing. +1. Make sure to know how much time it'll take to run all scheduled migrations +1. When migrating many columns, make sure it won't generate too many + dead tuples in the process (you may need to directly query the number of dead tuples + and adjust the scheduling according to this piece of data) +1. Make sure to discuss the numbers with a database specialist, the migration may add + more pressure on DB than you expect (measure on staging, + or ask someone to measure on production) [migrations-readme]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/migrations/README.md [issue-rspec-hooks]: https://gitlab.com/gitlab-org/gitlab-ce/issues/35351 diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index a315cfdc11643b659d7b7a59987d49f37ad7d541..ad49c77aac8094259e52b75b5f1bb893c5596196 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -219,28 +219,26 @@ below. - Every piece of documentation that comes with a new feature should declare the GitLab version that feature got introduced. Right below the heading add a - note: + blockquote: ```md > Introduced in GitLab 8.3. ``` -- Whenever possible, every feature should have a link to the MR, issue, or epic that introduced it. - The above note would be then transformed to: +- Whenever possible, every feature should have a link to the issue, MR or epic + (in that order) that introduced it. The above quote would be then transformed to: ```md - > [Introduced][ce-1242] in GitLab 8.3. + > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/1242) in GitLab 8.3. ``` - , where the [link identifier](#links) is named after the repository (CE) and - the MR number. - - If the feature is only available in GitLab Enterprise Edition, don't forget to mention the [paid tier](https://about.gitlab.com/handbook/marketing/product-marketing/#tiers) the feature is available in: ```md - > [Introduced][ee-1234] in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3. + > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/1242) + in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3. ``` ### Product badges diff --git a/doc/install/kubernetes/preparation/tiller.md b/doc/install/kubernetes/preparation/tiller.md index c92f8258e41d7c22813be05fdb098b4efed9a640..016aac2abebd7756aed384ac46194c5ba91b139b 100644 --- a/doc/install/kubernetes/preparation/tiller.md +++ b/doc/install/kubernetes/preparation/tiller.md @@ -4,7 +4,7 @@ To make use of Helm, you must have a [Kubernetes][k8s-io] cluster. Ensure you ca Helm consists of two parts, the `helm` client and a `tiller` server inside Kubernetes. -> **Note**: If you are not able to run tiller in your cluster, for example on OpenShift, it is possible to use [tiller locally](#local-tiller) and avoid deploying it into the cluster. This should only be used when Tiller cannot be normally deployed. +> **Note**: If you are not able to run Tiller in your cluster, for example on OpenShift, it is possible to use [Tiller locally](https://gitlab.com/charts/gitlab/tree/master/doc/helm#local-tiller) and avoid deploying it into the cluster. This should only be used when Tiller cannot be normally deployed. ## Initialize Helm and Tiller @@ -65,28 +65,32 @@ kubectl --username=admin --password=xxxxxxxxxxxxxx create -f rbac-config.yaml For other clusters like Amazon EKS, you can directly upload the RBAC configuration. +``` kubectl create -f rbac-config.yaml +``` ## Initialize Helm -Deploy Helm Tiller with a service account +Deploy Helm Tiller with a service account: ``` helm init --service-account tiller ``` -If your cluster -previously had Helm/Tiller installed, run the following to ensure that the deployed version of Tiller matches the local Helm version: +If your cluster previously had Helm/Tiller installed, +run the following to ensure that the deployed version of Tiller matches the local Helm version: ``` helm init --upgrade --service-account tiller ``` -### Patching Helm Tiller for EKS +### Patching Helm Tiller for Amazon EKS -Helm Tiller requires a flag to be enabled to work properly on EKS: +Helm Tiller requires a flag to be enabled to work properly on Amazon EKS: -`kubectl -n kube-system patch deployment tiller-deploy -p '{"spec": {"template": {"spec": {"automountServiceAccountToken": true}}}}'` +``` +kubectl -n kube-system patch deployment tiller-deploy -p '{"spec": {"template": {"spec": {"automountServiceAccountToken": true}}}}' +``` [helm]: https://helm.sh [helm-using]: https://docs.helm.sh/using_helm diff --git a/doc/security/rack_attack.md b/doc/security/rack_attack.md index c61729581e8d103ff10de5b57308492aa0c8febc..3efb19c1526f1530218e50de7e05c2a7034d64c4 100644 --- a/doc/security/rack_attack.md +++ b/doc/security/rack_attack.md @@ -9,13 +9,17 @@ In case you find throttling is not enough to protect you against abusive clients Rack Attack offers IP whitelisting, blacklisting, Fail2ban style filtering and tracking. +**Note:** Starting with 11.2, Rack Attack is disabled by default. To continue +using this feature, please enable it in your `gitlab.rb` by setting +`gitlab_rails['rack_attack_git_basic_auth'] = true`. + By default, user sign-in, user sign-up (if enabled), and user password reset is limited to 6 requests per minute. After trying for 6 times, the client will have to wait for the next minute to be able to try again. If you installed or upgraded GitLab by following the [official guides](../install/README.md) -this should be enabled by default. If your instance is not exposed to any incoming -connections, it is recommended to disable Rack Attack. +this should be disabled by default. If your instance is not exposed to any incoming +connections, it is recommended to leave Rack Attack disabled. For more information on how to use these options check out [rack-attack README](https://github.com/kickstarter/rack-attack/blob/master/README.md). @@ -45,7 +49,7 @@ For more information on how to use these options check out The following settings can be configured: -- `enabled`: By default this is set to `true`. Set this to `false` to disable Rack Attack. +- `enabled`: By default this is set to `false`. Set this to `true` to enable Rack Attack. - `ip_whitelist`: Whitelist any IPs from being blocked. They must be formatted as strings within a ruby array. For example, `["127.0.0.1", "127.0.0.2", "127.0.0.3"]`. - `maxretry`: The maximum amount of times a request can be made in the diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index de1d366adc31f0b779e5b4b007bda13eb62023b3..f5574506595e7b3b6b8974926bfc01f2da2f8190 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -591,7 +591,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac | `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). | | `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. | | `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. | -| `CODEQUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. | +| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. | | `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. | | `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. | | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. | diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index 0b38cde811d0fb2c0c2eab44d0757f3a5154a0c4..7c6a14cb104a6c66560db405d45968a45a984a3e 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -200,6 +200,12 @@ sudo apt-get install pgloader sudo -u git cp config/database.yml.postgresql config/database.yml sudo -u git -H chmod o-rwx config/database.yml ``` +1. Install Gems related to Postgresql + + ``` bash + sudo -u git -H rm .bundle/config + sudo -u git -H bundle install --deployment --without development test mysql aws kerberos + ``` 1. Run the following commands to prepare the schema: diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index 91cdef8d1dd79f2e9f87be76620e7ac790b8fbe6..96a08c04905a5315d64af588e1fff0bba7d7100c 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -68,6 +68,28 @@ Alternatively, you can follow [this detailed procedure from the GitLab Team Hand which also covers the case where you have projects hosted with [GitLab Pages](../project/pages/index.md). +## Private profile + +The following information will be hidden from the user profile page (https://gitlab.example.com/username) if this feature is enabled: + +- Atom feed +- Date when account is created +- Activity tab +- Groups tab +- Contributed projects tab +- Personal projects tab +- Snippets tab + +To enable private profile: + +1. Navigate to your personal [profile settings](#profile-settings). +1. Check the "Private profile" option. +1. Hit **Update profile settings**. + + +NOTE: **Note:** +You and GitLab admins can see your the abovementioned information on your profile even if it is private. + ## Troubleshooting ### Why do I keep getting signed out? diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index 9b4fdd65e2f785cea42ff8ad26c04c531a79a24e..25d6c34409c245151e25c536f8ab164c822f6d0c 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -48,6 +48,7 @@ the following table. | `api` | Grants complete access to the API (read/write) ([introduced][ce-5951] in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. | | `read_registry` | Allows to read [container registry] images if a project is private and authorization is required ([introduced][ce-11845] in GitLab 9.3). | | `sudo` | Allows performing API actions as any user in the system (if the authenticated user is an admin) ([introduced][ce-14838] in GitLab 10.2). | +| `read_repository` | Allows read-access to the repository through git clone. | [2fa]: ../account/two_factor_authentication.md [api]: ../../api/README.md diff --git a/doc/user/project/integrations/hangouts_chat.md b/doc/user/project/integrations/hangouts_chat.md new file mode 100644 index 0000000000000000000000000000000000000000..6ab44420a10472c4305ff8f5cd1e4329fed65a64 --- /dev/null +++ b/doc/user/project/integrations/hangouts_chat.md @@ -0,0 +1,27 @@ +# Hangouts Chat service + +The Hangouts Chat service sends notifications from GitLab to the room for which the webhook was created. + +## On Hangouts Chat + +1. Open the chat room in which you want to see the notifications. +1. From the chat room menu, select **Configure Webhooks**. +1. Click on **ADD WEBHOOK** and fill in the name of the bot that will post the messages. Optionally define avatar. +1. Click **SAVE** and copy the **Webhook URL** of your webhook. + +See also [the Hangouts Chat documentation for configuring incoming webhooks](https://developers.google.com/hangouts/chat/how-tos/webhooks) + +## On GitLab + +When you have the **Webhook URL** for your Hangouts Chat room webhook, you can setup the GitLab service. + +1. Navigate to the [Integrations page](project_services.md#accessing-the-project-services) in your project's settings, i.e. **Project > Settings > Integrations**. +1. Select the **Hangouts Chat** project service to configure it. +1. Check the **Active** checkbox to turn on the service. +1. Check the checkboxes corresponding to the GitLab events you want to receive. +1. Paste the **Webhook URL** that you copied from the Hangouts Chat configuration step. +1. Configure the remaining options and click `Save changes`. + +Your Hangouts Chat room will now start receiving GitLab event notifications as configured. + +![Hangouts Chat configuration](img/hangouts_chat_configuration.png) diff --git a/doc/user/project/integrations/img/hangouts_chat_configuration.png b/doc/user/project/integrations/img/hangouts_chat_configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..33fadbe6547e314d021aa83ba797a55474c96067 Binary files /dev/null and b/doc/user/project/integrations/img/hangouts_chat_configuration.png differ diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md index 8c51eb9915e1c0f84721efd6431a0d9db2e36279..05ee1b4e6d7c3b4672c87489a782174efc941006 100644 --- a/doc/user/project/integrations/project_services.md +++ b/doc/user/project/integrations/project_services.md @@ -35,6 +35,7 @@ Click on the service links to see further configuration instructions and details | External Wiki | Replaces the link to the internal wiki with a link to an external wiki | | Flowdock | Flowdock is a collaboration web app for technical teams | | Gemnasium _(Has been deprecated in GitLab 11.0)_ | Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities | +| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat | | [HipChat](hipchat.md) | Private group chat and IM | | [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway | | [JIRA](jira.md) | JIRA issue tracker | diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md index 860edb8e6f76e85b6ed1785000e929dd3e97067e..6dfdbe6c0d55d26ca5323c798575b3c11eca14f2 100644 --- a/doc/user/project/issue_board.md +++ b/doc/user/project/issue_board.md @@ -27,7 +27,7 @@ You create issues, host code, perform reviews, build, test, and deploy from one single platform. Issue Boards help you to visualize and manage the entire process _in_ GitLab. -With [Multiple Issue Boards](#multiple-issue-boards), available +With [Multiple Issue Boards](#use-cases-for-multiple-issue-boards), available only in [GitLab Enterprise Edition](#features-per-tier), you go even further, as you can not only keep yourself and your project organized from a broader perspective with one Issue Board per project, @@ -75,7 +75,7 @@ each team can have their own board to organize their workflow individually. #### Scrum team -With multiple Issue Boards, each team has one board. Now you can move issues through each +With Multiple Issue Boards, each team has one board. Now you can move issues through each part of the process. For instance: **To Do**, **Doing**, and **Done**. #### Organization of topics diff --git a/doc/user/project/merge_requests/img/merge_request.png b/doc/user/project/merge_requests/img/merge_request.png index f9ca63489531555646c5d079cdf3901b9c69f171..61b61122b112de09cc3a7a231f92ce9bead88b93 100644 Binary files a/doc/user/project/merge_requests/img/merge_request.png and b/doc/user/project/merge_requests/img/merge_request.png differ diff --git a/doc/user/project/milestones/img/milestones_new_group_milestone.png b/doc/user/project/milestones/img/milestones_new_group_milestone.png index 8780394d72e6baa6d0d6b4f770e9e667d9305e6b..b6defab101d470d5a319e673799f467b44010c7b 100644 Binary files a/doc/user/project/milestones/img/milestones_new_group_milestone.png and b/doc/user/project/milestones/img/milestones_new_group_milestone.png differ diff --git a/doc/user/project/milestones/img/milestones_new_project_milestone.png b/doc/user/project/milestones/img/milestones_new_project_milestone.png index ba058428dfafbd67f9aed6d3e977e647ec071da5..9aaff7dfef1b6a567ba1db729b5d91cd4a2bf526 100644 Binary files a/doc/user/project/milestones/img/milestones_new_project_milestone.png and b/doc/user/project/milestones/img/milestones_new_project_milestone.png differ diff --git a/doc/user/project/milestones/img/milestones_promote_milestone.png b/doc/user/project/milestones/img/milestones_promote_milestone.png index 99bee1240d4ba2ba1baf2362e2ebf778790638de..5e7f94c316fae03da9313478823986ea02d8aa3d 100644 Binary files a/doc/user/project/milestones/img/milestones_promote_milestone.png and b/doc/user/project/milestones/img/milestones_promote_milestone.png differ diff --git a/generator_templates/active_record/migration/create_table_migration.rb b/generator_templates/active_record/migration/create_table_migration.rb index 59a9d37df0fc42348083fbd3a5b7fd35b3fbc826..92e963911d0a9885e9356e21447df81e8c75c3f4 100644 --- a/generator_templates/active_record/migration/create_table_migration.rb +++ b/generator_templates/active_record/migration/create_table_migration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. diff --git a/generator_templates/active_record/migration/migration.rb b/generator_templates/active_record/migration/migration.rb index 08752b3af505756ee45f90a197f82c7fd4f84c69..38edab825509f209e232b890c9632bf69401ef7c 100644 --- a/generator_templates/active_record/migration/migration.rb +++ b/generator_templates/active_record/migration/migration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. diff --git a/generator_templates/rails/post_deployment_migration/migration.rb b/generator_templates/rails/post_deployment_migration/migration.rb index f2dff84b61837da93d0b75d6e0bbbf4c0613b3b3..353709f7c9ca1ca6699121fdcc6167791d069dc2 100644 --- a/generator_templates/rails/post_deployment_migration/migration.rb +++ b/generator_templates/rails/post_deployment_migration/migration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 3f3a95ea8e6e235a16a85d0a6cbb765d983eee00..e883687f2dba4738deb0a8bec5b89f75f20a4a20 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -30,7 +30,7 @@ module API end class User < UserBasic - expose :created_at + expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) } expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization end @@ -55,6 +55,7 @@ module API expose :can_create_project?, as: :can_create_project expose :two_factor_enabled?, as: :two_factor_enabled expose :external + expose :private_profile end class UserWithAdmin < UserPublic @@ -132,6 +133,7 @@ module API expose :star_count, :forks_count expose :last_activity_at + expose :namespace, using: 'API::Entities::NamespaceBasic' expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes def self.preload_relation(projects_relation, options = {}) @@ -194,7 +196,6 @@ module API expose :shared_runners_enabled expose :lfs_enabled?, as: :lfs_enabled expose :creator_id - expose :namespace, using: 'API::Entities::NamespaceBasic' expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? } expose :import_status expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] } @@ -529,6 +530,10 @@ module API class PipelineBasic < Grape::Entity expose :id, :sha, :ref, :status + + expose :web_url do |pipeline, _options| + Gitlab::Routing.url_helpers.project_pipeline_url(pipeline.project, pipeline) + end end class MergeRequestSimple < ProjectEntity @@ -1069,6 +1074,10 @@ module API expose :user, with: User expose :commit, with: Commit expose :pipeline, with: PipelineBasic + + expose :web_url do |job, _options| + Gitlab::Routing.url_helpers.project_job_url(job.project, job) + end end class Job < JobBasic diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 797b04df059374a8758993a588e7388ab78fd9e6..b4f441f6a4fbcfd2928fc05377553feb9fd95c07 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -34,11 +34,12 @@ module API optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id' optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)' + optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user' use :pagination end def find_groups(params, parent_id = nil) - find_params = params.slice(:all_available, :custom_attributes, :owned) + find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level) find_params[:parent] = find_group!(parent_id) if parent_id find_params[:all_available] = find_params.fetch(:all_available, current_user&.full_private_access?) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index f77374681488523b3471ea206493e1b636959c00..be17653dbb268fb3ccf72ac4d20013019832d65e 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -389,6 +389,7 @@ module API finder_params[:search] = params[:search] if params[:search] finder_params[:user] = params.delete(:user) if params[:user] finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes] + finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level] finder_params end diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb index e95b0dd52670baec135bb4a0ec6be5288abf210d..10c6e565f091e2f4fa303632748c0d5c238d0e45 100644 --- a/lib/api/jobs.rb +++ b/lib/api/jobs.rb @@ -54,7 +54,7 @@ module API pipeline = user_project.pipelines.find(params[:pipeline_id]) builds = pipeline.builds builds = filter_builds(builds, params[:scope]) - builds = builds.preload(:job_artifacts_archive) + builds = builds.preload(:job_artifacts_archive, project: [:namespace]) present paginate(builds), with: Entities::Job end diff --git a/lib/api/keys.rb b/lib/api/keys.rb index 767f27ef334122f129ede6bc6aaccc08cb7b5e0c..fd93f797f72bf71b87fd1ccc0ab751b7df0b94b0 100644 --- a/lib/api/keys.rb +++ b/lib/api/keys.rb @@ -12,7 +12,7 @@ module API key = Key.find(params[:id]) - present key, with: Entities::SSHKeyWithUser + present key, with: Entities::SSHKeyWithUser, current_user: current_user end end end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 889e3d4f8197f52214a73904589204b6088de121..eadde7b17bb5c5741d9f035610c758ec71b008c0 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -54,6 +54,7 @@ module API optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of' optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature' optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature' + optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user' use :optional_filter_params_ee end diff --git a/lib/api/services.rb b/lib/api/services.rb index 553e8dff4b970657bd6056ccebe380805bb0a81f..1f2bf546cd72b9861fec4607ad2daf7ad4f2a2c6 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -368,6 +368,14 @@ module API desc: "The project's slug on gemnasium.com" } ], + 'hangouts-chat' => [ + { + required: true, + name: :webhook, + type: String, + desc: 'The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces…' + } + ], 'hipchat' => [ { required: true, @@ -688,6 +696,7 @@ module API ExternalWikiService, FlowdockService, GemnasiumService, + HangoutsChatService, HipchatService, IrkerService, JiraService, diff --git a/lib/api/users.rb b/lib/api/users.rb index 5aaaf104dff7742d73b4c16f668ccca30aedd483..e83887b3e9eaa2f20a2f32fe7658d20706314686 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -42,6 +42,8 @@ module API optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups' optional :external, type: Boolean, desc: 'Flag indicating the user is an external user' optional :avatar, type: File, desc: 'Avatar image for user' + optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile' + optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user' all_or_none_of :extern_uid, :provider end @@ -96,7 +98,7 @@ module API entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin - users, options = with_custom_attributes(users, with: entity) + users, options = with_custom_attributes(users, { with: entity, current_user: current_user }) present paginate(users), options end @@ -113,7 +115,7 @@ module API user = User.find_by(id: params[:id]) not_found!('User') unless user && can?(current_user, :read_user, user) - opts = current_user&.admin? ? { with: Entities::UserWithAdmin } : { with: Entities::User } + opts = { with: current_user&.admin? ? Entities::UserWithAdmin : Entities::User, current_user: current_user } user, opts = with_custom_attributes(user, opts) present user, opts @@ -139,7 +141,7 @@ module API user = ::Users::CreateService.new(current_user, params).execute(skip_authorization: true) if user.persisted? - present user, with: Entities::UserPublic + present user, with: Entities::UserPublic, current_user: current_user else conflict!('Email has already been taken') if User .where(email: user.email) @@ -198,7 +200,7 @@ module API result = ::Users::UpdateService.new(current_user, user_params.except(:extern_uid, :provider).merge(user: user)).execute if result[:status] == :success - present user, with: Entities::UserPublic + present user, with: Entities::UserPublic, current_user: current_user else render_validation_error!(user) end @@ -545,7 +547,7 @@ module API Entities::UserPublic end - present current_user, with: entity + present current_user, with: entity, current_user: current_user end end diff --git a/lib/banzai/filter/absolute_link_filter.rb b/lib/banzai/filter/absolute_link_filter.rb index 1ec6201523f15ba97e96b46b62be3e67398aff23..04ec568eee3085c1886a9888f1fd3647bcca18e4 100644 --- a/lib/banzai/filter/absolute_link_filter.rb +++ b/lib/banzai/filter/absolute_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index b39b11009b3dda1e320c4bd9ce1b752d6896b17c..ad0806df8e69ed63285aaa3cd21e31608cdfe75b 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # Issues, Merge Requests, Snippets, Commits and Commit Ranges share diff --git a/lib/banzai/filter/ascii_doc_post_processing_filter.rb b/lib/banzai/filter/ascii_doc_post_processing_filter.rb index c9fcf057c5f41888646f6d3c400cfe9d59235e4b..88439f06b5f6b18fa0a0ba240335c2154df21222 100644 --- a/lib/banzai/filter/ascii_doc_post_processing_filter.rb +++ b/lib/banzai/filter/ascii_doc_post_processing_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class AsciiDocPostProcessingFilter < HTML::Pipeline::Filter diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb index 4a143baeef6bb6a990b859f1db6a5637b01a4b01..deda4b1872e149e1f0504b7055fdededd06213fb 100644 --- a/lib/banzai/filter/autolink_filter.rb +++ b/lib/banzai/filter/autolink_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb index 7108e828c6d06fb6b443f842b0f5c074e3471fe8..ad367cc5efe1306cc523893a1a52d20a6a709ab4 100644 --- a/lib/banzai/filter/blockquote_fence_filter.rb +++ b/lib/banzai/filter/blockquote_fence_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class BlockquoteFenceFilter < HTML::Pipeline::TextFilter diff --git a/lib/banzai/filter/color_filter.rb b/lib/banzai/filter/color_filter.rb index 6ab29ac281f6cc94bd14633643fd3ee426784eba..6d9bdb9cbd3c108ce4effb6be2250c2160cbf165 100644 --- a/lib/banzai/filter/color_filter.rb +++ b/lib/banzai/filter/color_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that renders `color` followed by a color "chip". diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb index 01b3b0dafb9fedb4dfc042e5d584e6afd6f70f7e..d6b46236a49405256c78dc54b768f3dd3a58db2f 100644 --- a/lib/banzai/filter/commit_range_reference_filter.rb +++ b/lib/banzai/filter/commit_range_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces commit range references with links. diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb index 8cd92a1adba2a54150b39990e4716f05d165cebd..c3e5ac41cb8c783c74ee981e97be7f9407b89c27 100644 --- a/lib/banzai/filter/commit_reference_filter.rb +++ b/lib/banzai/filter/commit_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces commit references with links. diff --git a/lib/banzai/filter/commit_trailers_filter.rb b/lib/banzai/filter/commit_trailers_filter.rb index 7b55e8b36f6d372e942bd4005387e2b1db797dcd..f49c4b403db432781dcc66583abed7cb1d075f44 100644 --- a/lib/banzai/filter/commit_trailers_filter.rb +++ b/lib/banzai/filter/commit_trailers_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces users' names and emails in commit trailers diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb index 4eccd9d5ed54406a74278686e80b54a928326ea9..c87948a30bf9fc567dd0dd47bf92ff921338838d 100644 --- a/lib/banzai/filter/emoji_filter.rb +++ b/lib/banzai/filter/emoji_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces :emoji: and unicode with images. diff --git a/lib/banzai/filter/epic_reference_filter.rb b/lib/banzai/filter/epic_reference_filter.rb index 265924abe24529e359b49058867d7cd71dd57ed6..e06e2fb3870e621b274796b3dc9ef84c88112652 100644 --- a/lib/banzai/filter/epic_reference_filter.rb +++ b/lib/banzai/filter/epic_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # The actual filter is implemented in the EE mixin diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb index ed01a72ff9f7cbd87db6c636c3281746ec518187..b4a7a44e109522e014c6b975a4d264bcc5924957 100644 --- a/lib/banzai/filter/external_issue_reference_filter.rb +++ b/lib/banzai/filter/external_issue_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces external issue tracker references with links. diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb index d6327ef31cbc41f9d58c8b40fda9523c423925c7..2e6d742de277f0664a54bb3f6d3bef0ade7f6977 100644 --- a/lib/banzai/filter/external_link_filter.rb +++ b/lib/banzai/filter/external_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML Filter to modify the attributes of external links diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb index bb9f488cd87b970972cbd7794734db8486fc2e39..0c1bbd2d250a8b5ae71d40863902e80ce347ca32 100644 --- a/lib/banzai/filter/gollum_tags_filter.rb +++ b/lib/banzai/filter/gollum_tags_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML Filter for parsing Gollum's tags in HTML. It's only parses the diff --git a/lib/banzai/filter/html_entity_filter.rb b/lib/banzai/filter/html_entity_filter.rb index e008fd428b06863da3a803eb624b6790586b4cad..406c2d3c96b627ac8584d728e3d217ebad417f45 100644 --- a/lib/banzai/filter/html_entity_filter.rb +++ b/lib/banzai/filter/html_entity_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'erb' module Banzai diff --git a/lib/banzai/filter/image_lazy_load_filter.rb b/lib/banzai/filter/image_lazy_load_filter.rb index 4cd9b02b76c45d1f937ace15f47498761afe4b58..afaee70f351c8ffef4745d257ddc93a606d14092 100644 --- a/lib/banzai/filter/image_lazy_load_filter.rb +++ b/lib/banzai/filter/image_lazy_load_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that moves the value of image `src` attributes to `data-src` @@ -5,7 +7,7 @@ module Banzai class ImageLazyLoadFilter < HTML::Pipeline::Filter def call doc.xpath('descendant-or-self::img').each do |img| - img['class'] ||= '' << 'lazy' + img.add_class('lazy') img['data-src'] = img['src'] img['src'] = LazyImageTagHelper.placeholder_image end diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb index f318c425962e060ea7811c3408f677f0516c2d28..884a94fb761dc51911c7b92bde1ac6fabd4cd0e4 100644 --- a/lib/banzai/filter/image_link_filter.rb +++ b/lib/banzai/filter/image_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that wraps links around inline images. diff --git a/lib/banzai/filter/inline_diff_filter.rb b/lib/banzai/filter/inline_diff_filter.rb index 73e82a4d7e3a8f306fffca102101b852da9d3826..e9ddc6e0e3d4f4649c3dd54a183edb85ea0c8620 100644 --- a/lib/banzai/filter/inline_diff_filter.rb +++ b/lib/banzai/filter/inline_diff_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class InlineDiffFilter < HTML::Pipeline::Filter diff --git a/lib/banzai/filter/issuable_reference_filter.rb b/lib/banzai/filter/issuable_reference_filter.rb index 7addf09be7328ee52249f5b7c2ae7f4716ee9c29..2963cba91e872f470ebaf07b031c703beaa4e310 100644 --- a/lib/banzai/filter/issuable_reference_filter.rb +++ b/lib/banzai/filter/issuable_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class IssuableReferenceFilter < AbstractReferenceFilter diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb index 1a4152325456f1d3718310ca7232efce6095a046..d7fe012883dbc16c05641bbd94486e28190d419f 100644 --- a/lib/banzai/filter/issuable_state_filter.rb +++ b/lib/banzai/filter/issuable_state_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that appends state information to issuable links. diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb index 6877cae8c55e234b4a855fae26741a4956ec4241..f85be0429994a11fdd8023dfa67530456d1817aa 100644 --- a/lib/banzai/filter/issue_reference_filter.rb +++ b/lib/banzai/filter/issue_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces issue references with links. References to diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb index a5f38046a4330f615de534282272f3a92999bcc5..b92e9e55bb92c9eb4fdd54c489ea0b8fb3077260 100644 --- a/lib/banzai/filter/label_reference_filter.rb +++ b/lib/banzai/filter/label_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces label references with links. diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb index 944363f17d341c190316fa2c932a97857ceb3805..cdf758472c13999f26433d94dd1bd31b1b40a988 100644 --- a/lib/banzai/filter/markdown_filter.rb +++ b/lib/banzai/filter/markdown_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class MarkdownFilter < HTML::Pipeline::TextFilter diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb index b6e784c886ba8a65dae6122090078aa0300c81fc..9d1bc3cf60ca31789ae4250f7286dd7d6bb478d8 100644 --- a/lib/banzai/filter/math_filter.rb +++ b/lib/banzai/filter/math_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb index 10c40568006c8b374a1571d38f1dbd71069098f3..7098767b58322b775bd44a0bbd039bf796a2243f 100644 --- a/lib/banzai/filter/merge_request_reference_filter.rb +++ b/lib/banzai/filter/merge_request_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces merge request references with links. References diff --git a/lib/banzai/filter/mermaid_filter.rb b/lib/banzai/filter/mermaid_filter.rb index 65c131e08d9cf4db44a841c90f4a2a209d627be5..7c8b165a330aef467a6b5c52fb19ba2566760761 100644 --- a/lib/banzai/filter/mermaid_filter.rb +++ b/lib/banzai/filter/mermaid_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class MermaidFilter < HTML::Pipeline::Filter diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb index af8448937b372fe55e6c83429c1e7449db5e0ab7..328c8c1803be8d48cfa6c643b62713ce4efd9de3 100644 --- a/lib/banzai/filter/milestone_reference_filter.rb +++ b/lib/banzai/filter/milestone_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces milestone references with links. diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb index 28933c789667292209ec93ef8d853275174cbfd1..caba8955bac106566efce98aa5c2d5a06c44364f 100644 --- a/lib/banzai/filter/plantuml_filter.rb +++ b/lib/banzai/filter/plantuml_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "nokogiri" require "asciidoctor-plantuml/plantuml" diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb index caf11fe94c48feca411efa0b58a2936561033c36..1f091f594f86d62c2a4d6b3ac138cf7f59e198c7 100644 --- a/lib/banzai/filter/redactor_filter.rb +++ b/lib/banzai/filter/redactor_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that removes references to records that the current user does diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb index 2411dd2cdfc60936483229fe07edadf42f96275f..e5164e7f72a94c034e74c31b7ea1ef411b3b4e9d 100644 --- a/lib/banzai/filter/reference_filter.rb +++ b/lib/banzai/filter/reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # Base class for GitLab Flavored Markdown reference filters. diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index 262458a872a56a73e76c28d742dcb7736e77b229..8e838d04badfe11c46ba6071723861849a62ae5d 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index 8275bb9e14928d56e72a75707eee3f3b082fdc4e..8ba09290e6dd784378028bac01ad7e231b089d70 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # Sanitize HTML @@ -11,7 +13,7 @@ module Banzai def whitelist strong_memoize(:whitelist) do - customize_whitelist(super.dup) + customize_whitelist(super.deep_dup) end end diff --git a/lib/banzai/filter/set_direction_filter.rb b/lib/banzai/filter/set_direction_filter.rb index c2976aeb7c6dff663fa95e389722b2ed2a7cdc4f..45b259a2faf1bb9b7610f2db75aeb64cd230d43e 100644 --- a/lib/banzai/filter/set_direction_filter.rb +++ b/lib/banzai/filter/set_direction_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that sets dir="auto" for RTL languages support diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb index 881e10afb9f21e78e9ede4b2c891c8cd9e36e838..f4b6edb61742d2e6dbb016eb58808b2f40d68157 100644 --- a/lib/banzai/filter/snippet_reference_filter.rb +++ b/lib/banzai/filter/snippet_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces snippet references with links. References to diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb index 6dbf0d68fe8f0d9c05a022ef610341bb81b99a53..8a7f9045c24f218f2d6e9d5344c6ff7c4379e8b9 100644 --- a/lib/banzai/filter/syntax_highlight_filter.rb +++ b/lib/banzai/filter/syntax_highlight_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rouge/plugins/common_mark' require 'rouge/plugins/redcarpet' @@ -15,7 +17,7 @@ module Banzai end def highlight_node(node) - css_classes = 'code highlight js-syntax-highlight' + css_classes = +'code highlight js-syntax-highlight' lang = node.attr('lang') retried = false diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb index b32660a83418c6a6cccbd95c2241ecb4a9f226b5..c6d1e028eaad52fe4b0b1ce0e9c1d6f695a59156 100644 --- a/lib/banzai/filter/table_of_contents_filter.rb +++ b/lib/banzai/filter/table_of_contents_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that adds an anchor child element to all Headers in a @@ -19,7 +21,7 @@ module Banzai def call return doc if context[:no_header_anchors] - result[:toc] = "" + result[:toc] = +"" headers = Hash.new(0) header_root = current_header = HeaderNode.new diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb index 9fa5f589f3ec258aed87bae0863a45b66a45908a..ef35a49edcba3a41d40c20a811fe6ff59634fe67 100644 --- a/lib/banzai/filter/task_list_filter.rb +++ b/lib/banzai/filter/task_list_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'task_list/filter' module Banzai diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb index c7fa8a8119f6d5f971ef9c705d0581738e7ce8d1..11960047e5baedcb9ae53dd86892c56ca494115a 100644 --- a/lib/banzai/filter/user_reference_filter.rb +++ b/lib/banzai/filter/user_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces user or group references with links. diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb index 35cb10eae5dd068bd00bd3141c2314bdaafe8905..0fb59c914c37c8b3a2bc3c7eaf7dd4ad078aa01d 100644 --- a/lib/banzai/filter/video_link_filter.rb +++ b/lib/banzai/filter/video_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # Find every image that isn't already wrapped in an `a` tag, and that has diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb index 269d5bf74fa973603cf498e1ed990b362a51e70e..870721f895df44159f448b0dcdf9f362b4335db8 100644 --- a/lib/banzai/filter/wiki_link_filter.rb +++ b/lib/banzai/filter/wiki_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/yaml_front_matter_filter.rb b/lib/banzai/filter/yaml_front_matter_filter.rb index 58e3e81209eea75dd8eebc7d34ddc35aff8d59bc..295964dd75d4b009b591026dfbe1dde4fee66436 100644 --- a/lib/banzai/filter/yaml_front_matter_filter.rb +++ b/lib/banzai/filter/yaml_front_matter_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class YamlFrontMatterFilter < HTML::Pipeline::Filter diff --git a/lib/feature.rb b/lib/feature.rb index 314ae224d9029ba153a28bee13815ea72d7fe72f..d27b2b0e72fd445ea1ef38a51cdcd6985bbd4641 100644 --- a/lib/feature.rb +++ b/lib/feature.rb @@ -39,7 +39,7 @@ class Feature # Flipper creates on-memory features when asked for a not-yet-created one. # If we want to check if a feature has been actually set, we look for it # on the persisted features list. - persisted_names.include?(feature.name) + persisted_names.include?(feature.name.to_s) end def enabled?(key, thing = nil) diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 7de66539848115f4c1384285c00bb89759085571..111e18b20763fa54ad0ed8f857a45eeb0734f664 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -14,23 +14,8 @@ module Gitlab DEFAULT_SCOPES = [:api].freeze class << self - def omniauth_customized_providers - @omniauth_customized_providers ||= %w[bitbucket jwt] - end - - def omniauth_setup_providers(provider_names) - provider_names.each do |provider| - omniauth_setup_a_provider(provider) - end - end - - def omniauth_setup_a_provider(provider) - case provider - when 'kerberos' - require 'omniauth-kerberos' - when *omniauth_customized_providers - require_dependency "omni_auth/strategies/#{provider}" - end + def omniauth_enabled? + Gitlab.config.omniauth.enabled end def find_for_git_client(login, password, project:, ip:) diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb index 5fb61ffe00d7be5b62d179c7b9d464e4ddc09304..e73743944a9d6354037f79734190096e05be4d36 100644 --- a/lib/gitlab/auth/o_auth/provider.rb +++ b/lib/gitlab/auth/o_auth/provider.rb @@ -30,7 +30,7 @@ module Gitlab def self.enabled?(name) return true if name == 'database' - providers.include?(name.to_sym) + Gitlab::Auth.omniauth_enabled? && providers.include?(name.to_sym) end def self.ldap_provider?(name) diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index 872e70f9a5d1f2fbb00480f87a465729b1872073..8eacad078c8ac7c8d4e1d59c610c2dacef74a350 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -61,6 +61,10 @@ module Gitlab @version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1] end + def self.postgresql_9_or_less? + postgresql? && version.to_f < 10 + end + def self.join_lateral_supported? postgresql? && version.to_f >= 9.3 end @@ -69,6 +73,28 @@ module Gitlab postgresql? && version.to_f >= 9.4 end + def self.pg_stat_wal_receiver_supported? + postgresql? && version.to_f >= 9.6 + end + + # map some of the function names that changed between PostgreSQL 9 and 10 + # https://wiki.postgresql.org/wiki/New_in_postgres_10 + def self.pg_wal_lsn_diff + Gitlab::Database.postgresql_9_or_less? ? 'pg_xlog_location_diff' : 'pg_wal_lsn_diff' + end + + def self.pg_current_wal_insert_lsn + Gitlab::Database.postgresql_9_or_less? ? 'pg_current_xlog_insert_location' : 'pg_current_wal_insert_lsn' + end + + def self.pg_last_wal_receive_lsn + Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_receive_location' : 'pg_last_wal_receive_lsn' + end + + def self.pg_last_wal_replay_lsn + Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_replay_location' : 'pg_last_wal_replay_lsn' + end + def self.nulls_last_order(field, direction = 'ASC') order = "#{field} #{direction}" diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 5b955753a9233e078e26dc63bd1d4312580101e0..0356e8efc5c4e73a39e4a0515ba873e6d3f28bcd 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -353,8 +353,6 @@ module Gitlab # offset: 5, # after: Time.new(2016, 4, 21, 14, 32, 10) # ) - # - # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/446 def log(options) default_options = { limit: 10, @@ -826,6 +824,10 @@ module Gitlab end end + # This method, fetch_ref, is used from within + # Gitlab::Git::OperationService. OperationService will eventually only + # exist in gitaly-ruby. When we delete OperationService from gitlab-ce + # we can also remove fetch_ref. def fetch_ref(source_repository, source_ref:, target_ref:) Gitlab::Git.check_namespace!(source_repository) source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository) @@ -1025,8 +1027,8 @@ module Gitlab end def clean_stale_repository_files - gitaly_migrate(:repository_cleanup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| - gitaly_repository_client.cleanup if is_enabled && exists? + wrapped_gitaly_errors do + gitaly_repository_client.cleanup if exists? end rescue Gitlab::Git::CommandError => e # Don't fail if we can't cleanup Rails.logger.error("Unable to clean repository on storage #{storage} with relative path #{relative_path}: #{e.message}") @@ -1253,14 +1255,6 @@ module Gitlab run_git(args, env: source_repository.fetch_env) end - def rugged_add_remote(remote_name, url, mirror_refmap) - rugged.remotes.create(remote_name, url) - - set_remote_as_mirror(remote_name, refmap: mirror_refmap) if mirror_refmap - rescue Rugged::ConfigError - remote_update(remote_name, url: url) - end - def gitaly_delete_refs(*ref_names) gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any? end diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb index 8ee46b5983084fccf8841300a3b922be6a059064..9d992be66eb33a480cbbeda9308902ea17c90e64 100644 --- a/lib/gitlab/git/wiki.rb +++ b/lib/gitlab/git/wiki.rb @@ -44,9 +44,9 @@ module Gitlab end end - def pages(limit: nil) + def pages(limit: 0) @repository.wrapped_gitaly_errors do - gitaly_get_all_pages + gitaly_get_all_pages(limit: limit) end end @@ -158,8 +158,8 @@ module Gitlab Gitlab::Git::WikiFile.new(wiki_file) end - def gitaly_get_all_pages - gitaly_wiki_client.get_all_pages.map do |wiki_page, version| + def gitaly_get_all_pages(limit: 0) + gitaly_wiki_client.get_all_pages(limit: limit).map do |wiki_page, version| Gitlab::Git::WikiPage.new(wiki_page, version) end end diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 58a4060cc96f04f0630f0f7a85d20c53cb11a488..c27972a84a431254beeba9b152b66d7a8b3138a6 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -407,7 +407,7 @@ module Gitlab # The default timeout on all Gitaly calls def self.default_timeout - return 0 if Sidekiq.server? + return no_timeout if Sidekiq.server? timeout(:gitaly_timeout_default) end @@ -420,6 +420,10 @@ module Gitlab timeout(:gitaly_timeout_medium) end + def self.no_timeout + 0 + end + def self.timeout(timeout_name) Gitlab::CurrentSettings.current_application_settings[timeout_name] end diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index 64b9af4d70c9355edd418e9f7958f8606cd4d2de..2956ed4b9118ebc1b31886d5a6e6407eb51894dd 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -202,7 +202,7 @@ module Gitlab save_path, :create_bundle, Gitaly::CreateBundleRequest, - GitalyClient.default_timeout + GitalyClient.no_timeout ) end @@ -220,7 +220,7 @@ module Gitlab bundle_path, :create_repository_from_bundle, Gitaly::CreateRepositoryFromBundleRequest, - GitalyClient.default_timeout + GitalyClient.no_timeout ) end @@ -245,7 +245,7 @@ module Gitlab :repository_service, :create_repository_from_snapshot, request, - timeout: GitalyClient.default_timeout + timeout: GitalyClient.no_timeout ) end diff --git a/lib/gitlab/gitaly_client/wiki_service.rb b/lib/gitlab/gitaly_client/wiki_service.rb index 6cb049c1f68c2913e22aa8ce9978ecc40bc35e18..75be7d1f5a0b7560cf18851280f14b393ed12c0d 100644 --- a/lib/gitlab/gitaly_client/wiki_service.rb +++ b/lib/gitlab/gitaly_client/wiki_service.rb @@ -85,8 +85,8 @@ module Gitlab wiki_page_from_iterator(response) end - def get_all_pages - request = Gitaly::WikiGetAllPagesRequest.new(repository: @gitaly_repo) + def get_all_pages(limit: 0) + request = Gitaly::WikiGetAllPagesRequest.new(repository: @gitaly_repo, limit: limit) response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_all_pages, request, timeout: GitalyClient.medium_timeout) pages = [] diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb index 22719e9a003902ae9e74cc1c9dc9dd8c145c411b..38ef12491df91551ad44ee42714740fd2a1641b5 100644 --- a/lib/gitlab/gitlab_import/client.rb +++ b/lib/gitlab/gitlab_import/client.rb @@ -32,15 +32,15 @@ module Gitlab api.get("/api/v4/user").parsed end - def issues(project_identifier) - lazy_page_iterator(PER_PAGE) do |page| - api.get("/api/v4/projects/#{project_identifier}/issues?per_page=#{PER_PAGE}&page=#{page}").parsed + def issues(project_identifier, **kwargs) + lazy_page_iterator(**kwargs) do |page, per_page| + api.get("/api/v4/projects/#{project_identifier}/issues?per_page=#{per_page}&page=#{page}").parsed end end - def issue_comments(project_identifier, issue_id) - lazy_page_iterator(PER_PAGE) do |page| - api.get("/api/v4/projects/#{project_identifier}/issues/#{issue_id}/notes?per_page=#{PER_PAGE}&page=#{page}").parsed + def issue_comments(project_identifier, issue_id, **kwargs) + lazy_page_iterator(**kwargs) do |page, per_page| + api.get("/api/v4/projects/#{project_identifier}/issues/#{issue_id}/notes?per_page=#{per_page}&page=#{page}").parsed end end @@ -48,23 +48,27 @@ module Gitlab api.get("/api/v4/projects/#{id}").parsed end - def projects - lazy_page_iterator(PER_PAGE) do |page| - api.get("/api/v4/projects?per_page=#{PER_PAGE}&page=#{page}").parsed + def projects(**kwargs) + lazy_page_iterator(**kwargs) do |page, per_page| + api.get("/api/v4/projects?per_page=#{per_page}&page=#{page}&simple=true&membership=true").parsed end end private - def lazy_page_iterator(per_page) + def lazy_page_iterator(starting_page: 1, page_limit: nil, per_page: PER_PAGE) Enumerator.new do |y| - page = 1 + page = starting_page + page_limit = (starting_page - 1) + page_limit if page_limit + loop do - items = yield(page) + items = yield(page, per_page) + items.each do |item| y << item end - break if items.empty? || items.size < per_page + + break if items.empty? || items.size < per_page || (page_limit && page >= page_limit) page += 1 end diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb index f3d7407383cdb1ef24eb59ddd9966e8d29641e22..d0527f014a7c4029da5d159560fccc155f6537c8 100644 --- a/lib/gitlab/import_export/merge_request_parser.rb +++ b/lib/gitlab/import_export/merge_request_parser.rb @@ -25,6 +25,7 @@ module Gitlab @project.repository.create_branch(@merge_request.target_branch, @merge_request.target_branch_sha) end + # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1295 def fetch_ref @project.repository.fetch_ref(@project.repository, source_ref: @diff_head_sha, target_ref: @merge_request.source_branch) end diff --git a/lib/gitlab/omniauth_initializer.rb b/lib/gitlab/omniauth_initializer.rb index a71acda87016d61345d63c3c1e188eadcbe1c308..f33ea0880df8f927ccd8a38be7f78b082e476f51 100644 --- a/lib/gitlab/omniauth_initializer.rb +++ b/lib/gitlab/omniauth_initializer.rb @@ -1,23 +1,21 @@ module Gitlab class OmniauthInitializer - def self.enabled? - Gitlab.config.omniauth.enabled || - Gitlab.config.omniauth.auto_sign_in_with_provider.present? - end - def initialize(devise_config) @devise_config = devise_config end def execute(providers) providers.each do |provider| - add_provider(provider['name'].to_sym, *arguments_for(provider)) + name = provider['name'].to_sym + + add_provider_to_devise(name, *arguments_for(provider)) + setup_provider(name) end end private - def add_provider(*args) + def add_provider_to_devise(*args) @devise_config.omniauth(*args) end @@ -76,5 +74,23 @@ module Gitlab end end end + + def omniauth_customized_providers + @omniauth_customized_providers ||= build_omniauth_customized_providers + end + + # We override this in EE + def build_omniauth_customized_providers + %i[bitbucket jwt] + end + + def setup_provider(provider) + case provider + when :kerberos + require 'omniauth-kerberos' + when *omniauth_customized_providers + require_dependency "omni_auth/strategies/#{provider}" + end + end end end diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index dff0c97eeb4a14cb7e50e33f4401a4a814fd27ab..22c9638ecc0cae043b853a3dca8769d96c5eb039 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -95,7 +95,7 @@ module Gitlab gravatar_enabled: Gitlab::CurrentSettings.gravatar_enabled?, ldap_enabled: Gitlab.config.ldap.enabled, mattermost_enabled: Gitlab.config.mattermost.enabled, - omniauth_enabled: Gitlab.config.omniauth.enabled, + omniauth_enabled: Gitlab::Auth.omniauth_enabled?, reply_by_email_enabled: Gitlab::IncomingEmail.enabled?, signup_enabled: Gitlab::CurrentSettings.allow_signup? } diff --git a/lib/support/nginx/registry-ssl b/lib/support/nginx/registry-ssl index 92511e268615f4c181339ecf802f8641b9310cee..908d26a0da244a71f18b4b7fb966be3eae1d0492 100644 --- a/lib/support/nginx/registry-ssl +++ b/lib/support/nginx/registry-ssl @@ -10,7 +10,7 @@ server { listen *:80; server_name registry.gitlab.example.com; server_tokens off; ## Don't show the nginx version number, a security best practice - return 301 https://$http_host:$request_uri; + return 301 https://$http_host$request_uri; access_log /var/log/nginx/gitlab_registry_access.log gitlab_access; error_log /var/log/nginx/gitlab_registry_error.log; } diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake index 6de739e95158eb444c4e2101cd4f83781078fb83..e97d77d20e0161e4e9074d74772767d3638fd9a3 100644 --- a/lib/tasks/gitlab/info.rake +++ b/lib/tasks/gitlab/info.rake @@ -54,8 +54,8 @@ namespace :gitlab do puts "HTTP Clone URL:\t#{http_clone_url}" puts "SSH Clone URL:\t#{ssh_clone_url}" puts "Using LDAP:\t#{Gitlab.config.ldap.enabled ? "yes".color(:green) : "no"}" - puts "Using Omniauth:\t#{Gitlab.config.omniauth.enabled ? "yes".color(:green) : "no"}" - puts "Omniauth Providers: #{omniauth_providers.join(', ')}" if Gitlab.config.omniauth.enabled + puts "Using Omniauth:\t#{Gitlab::Auth.omniauth_enabled? ? "yes".color(:green) : "no"}" + puts "Omniauth Providers: #{omniauth_providers.join(', ')}" if Gitlab::Auth.omniauth_enabled? # check Gitolite version gitlab_shell_version_file = "#{Gitlab.config.gitlab_shell.hooks_path}/../VERSION" diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 53eeb67672f9de8fad422d588b002c79c072a03a..a313713a9a828e63fa745f6955056e400e2498e8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8,8 +8,6 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-07-10 16:02-0700\n" -"PO-Revision-Date: 2018-07-10 16:02-0700\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "Language: \n" @@ -240,6 +238,12 @@ msgstr "" msgid "A user with write access to the source branch selected this option" msgstr "" +msgid "About GitLab" +msgstr "" + +msgid "About GitLab CE" +msgstr "" + msgid "About auto deploy" msgstr "" @@ -312,6 +316,15 @@ msgstr "" msgid "Add users to group" msgstr "" +msgid "Admin Area" +msgstr "" + +msgid "Admin Overview" +msgstr "" + +msgid "Admin area" +msgstr "" + msgid "AdminArea|Stop all jobs" msgstr "" @@ -465,7 +478,7 @@ msgstr "" msgid "An error occurred while importing project: ${details}" msgstr "" -msgid "An error occurred while loading commits" +msgid "An error occurred while loading commit signatures" msgstr "" msgid "An error occurred while loading diff" @@ -504,6 +517,9 @@ msgstr "" msgid "Anonymous" msgstr "" +msgid "Anti-spam verification" +msgstr "" + msgid "Any" msgstr "" @@ -597,6 +613,9 @@ msgstr "" msgid "Authentication Log" msgstr "" +msgid "Authentication log" +msgstr "" + msgid "Author" msgstr "" @@ -621,6 +640,9 @@ msgstr "" msgid "Authors: %{authors}" msgstr "" +msgid "Auto DevOps" +msgstr "" + msgid "Auto DevOps enabled" msgstr "" @@ -678,6 +700,9 @@ msgstr "" msgid "Average per day: %{average}" msgstr "" +msgid "Background Jobs" +msgstr "" + msgid "Background color" msgstr "" @@ -768,6 +793,9 @@ msgstr "" msgid "Bitbucket import" msgstr "" +msgid "Blog" +msgstr "" + msgid "Boards" msgstr "" @@ -1532,6 +1560,9 @@ msgstr "" msgid "ClusterIntegration|sign up" msgstr "" +msgid "Cohorts" +msgstr "" + msgid "Collapse" msgstr "" @@ -1758,6 +1789,9 @@ msgstr "" msgid "Control the display of third party offers." msgstr "" +msgid "ConvDev Index" +msgstr "" + msgid "Copy URL to clipboard" msgstr "" @@ -1794,6 +1828,9 @@ msgstr "" msgid "Create a new branch and merge request" msgstr "" +msgid "Create a new issue" +msgstr "" + msgid "Create a personal access token on your account to pull or push via %{protocol}." msgstr "" @@ -1920,6 +1957,9 @@ msgstr "" msgid "CycleAnalyticsStage|Test" msgstr "" +msgid "Dashboard" +msgstr "" + msgid "DashboardProjects|All" msgstr "" @@ -2402,6 +2442,9 @@ msgstr "" msgid "Every week (Sundays at 4:00am)" msgstr "" +msgid "Everyone can contribute" +msgstr "" + msgid "Expand" msgstr "" @@ -2411,6 +2454,9 @@ msgstr "" msgid "Expand sidebar" msgstr "" +msgid "Explore" +msgstr "" + msgid "Explore GitLab" msgstr "" @@ -2426,6 +2472,9 @@ msgstr "" msgid "Explore public groups" msgstr "" +msgid "Facebook" +msgstr "" + msgid "Failed" msgstr "" @@ -2677,6 +2726,9 @@ msgstr "" msgid "Group avatar" msgstr "" +msgid "Group details" +msgstr "" + msgid "Group info:" msgstr "" @@ -3003,6 +3055,9 @@ msgstr "" msgid "Koding" msgstr "" +msgid "Koding Dashboard" +msgstr "" + msgid "Kubernetes" msgstr "" @@ -3125,6 +3180,9 @@ msgstr "" msgid "Leave the \"File type\" and \"Delivery method\" options on their default values." msgstr "" +msgid "LinkedIn" +msgstr "" + msgid "List" msgstr "" @@ -3164,9 +3222,15 @@ msgstr "" msgid "Locked to current projects" msgstr "" +msgid "Logs" +msgstr "" + msgid "Make sure you're logged into the account that owns the projects you'd like to import." msgstr "" +msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki." +msgstr "" + msgid "Manage access" msgstr "" @@ -3347,6 +3411,9 @@ msgstr "" msgid "Monitoring" msgstr "" +msgid "More" +msgstr "" + msgid "More actions" msgstr "" @@ -3389,6 +3456,9 @@ msgstr "" msgid "Nav|Sign out and sign in with a different account" msgstr "" +msgid "Network" +msgstr "" + msgid "New" msgstr "" @@ -3463,6 +3533,9 @@ msgstr "" msgid "New tag" msgstr "" +msgid "New..." +msgstr "" + msgid "No" msgstr "" @@ -3643,6 +3716,12 @@ msgstr "" msgid "Open in Xcode" msgstr "" +msgid "Open sidebar" +msgstr "" + +msgid "Open source software to collaborate on code" +msgstr "" + msgid "OpenedNDaysAgo|Opened" msgstr "" @@ -3943,6 +4022,9 @@ msgstr "" msgid "Profile" msgstr "" +msgid "Profile Settings" +msgstr "" + msgid "Profiles|Account scheduled for removal." msgstr "" @@ -4273,6 +4355,9 @@ msgstr "" msgid "Remove project" msgstr "" +msgid "Reply to this email directly or %{view_it_on_gitlab}." +msgstr "" + msgid "Repository" msgstr "" @@ -4294,6 +4379,9 @@ msgstr "" msgid "Request Access" msgstr "" +msgid "Requests Profiles" +msgstr "" + msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab." msgstr "" @@ -4542,6 +4630,9 @@ msgstr "" msgid "Shared Runners" msgstr "" +msgid "Sherlock Transactions" +msgstr "" + msgid "Show command" msgstr "" @@ -4571,6 +4662,12 @@ msgstr[1] "" msgid "Side-by-side" msgstr "" +msgid "Sign in" +msgstr "" + +msgid "Sign in / Register" +msgstr "" + msgid "Sign out" msgstr "" @@ -4784,6 +4881,9 @@ msgstr "" msgid "Status" msgstr "" +msgid "Stop impersonation" +msgstr "" + msgid "Stop this environment" msgstr "" @@ -4817,6 +4917,9 @@ msgstr "" msgid "System Hooks" msgstr "" +msgid "System Info" +msgstr "" + msgid "Tag (%{tag_count})" msgid_plural "Tags (%{tag_count})" msgstr[0] "" @@ -5056,6 +5159,9 @@ msgstr "" msgid "This directory" msgstr "" +msgid "This group" +msgstr "" + msgid "This group does not provide any group Runners yet." msgstr "" @@ -5345,12 +5451,18 @@ msgstr "" msgid "Todo" msgstr "" +msgid "Todos" +msgstr "" + msgid "Toggle Sidebar" msgstr "" msgid "Toggle discussion" msgstr "" +msgid "Toggle navigation" +msgstr "" + msgid "Toggle sidebar" msgstr "" @@ -5387,7 +5499,7 @@ msgstr "" msgid "Try again" msgstr "" -msgid "URL for Bitbucket Server" +msgid "Twitter" msgstr "" msgid "Unable to load the diff. %{button_try_again}" @@ -5479,6 +5591,9 @@ msgstr "" msgid "Use your global notification setting" msgstr "" +msgid "User Settings" +msgstr "" + msgid "User and IP Rate Limits" msgstr "" @@ -5512,6 +5627,9 @@ msgstr "" msgid "View group labels" msgstr "" +msgid "View it on GitLab" +msgstr "" + msgid "View jobs" msgstr "" @@ -5554,6 +5672,9 @@ msgstr "" msgid "Want to see the data? Please ask an administrator for access." msgstr "" +msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed." +msgstr "" + msgid "We don't have enough data to show this stage." msgstr "" @@ -5827,9 +5948,18 @@ msgstr "" msgid "You'll need to use different branch names to get a valid comparison." msgstr "" +msgid "You're receiving this email because %{reason}." +msgstr "" + +msgid "You're receiving this email because of your account on %{host}." +msgstr "" + msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} · %{help_link}" msgstr "" +msgid "YouTube" +msgstr "" + msgid "Your Groups" msgstr "" @@ -5919,6 +6049,9 @@ msgstr "" msgid "here" msgstr "" +msgid "https://your-bitbucket-server" +msgstr "" + msgid "import flow" msgstr "" @@ -6145,6 +6278,9 @@ msgstr "" msgid "uses Kubernetes clusters to deploy your code!" msgstr "" +msgid "view it on GitLab" +msgstr "" + msgid "with %{additions} additions, %{deletions} deletions." msgstr "" diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/menu/side.rb index c14a835c2c933b6b12e1aabce9358abb5dffd779..354ccec2a5a6c54cec7038ac81f60f0af69ddeed 100644 --- a/qa/qa/page/menu/side.rb +++ b/qa/qa/page/menu/side.rb @@ -5,8 +5,8 @@ module QA view 'app/views/layouts/nav/sidebar/_project.html.haml' do element :settings_item element :settings_link, 'link_to edit_project_path' - element :repository_link, "title: 'Repository'" - element :pipelines_settings_link, "title: 'CI / CD'" + element :repository_link, "title: _('Repository')" + element :pipelines_settings_link, "title: _('CI / CD')" element :operations_kubernetes_link, "title: _('Kubernetes')" element :issues_link, /link_to.*shortcuts-issues/ element :issues_link_text, "Issues" @@ -14,7 +14,7 @@ module QA element :merge_requests_link_text, "Merge Requests" element :top_level_items, '.sidebar-top-level-items' element :operations_section, "class: 'shortcuts-operations'" - element :activity_link, "title: 'Activity'" + element :activity_link, "title: _('Activity')" element :wiki_link_text, "Wiki" element :milestones_link end diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb index 1d3dad4cda0f7ca1479a153f97b364c1aae6930a..c751b472535c1184e03f7bd3489c7690bd286b7b 100644 --- a/qa/qa/page/project/show.rb +++ b/qa/qa/page/project/show.rb @@ -14,7 +14,7 @@ module QA view 'app/views/layouts/header/_new_dropdown.haml' do element :new_menu_toggle - element :new_issue_link, "link_to 'New issue', new_project_issue_path(@project)" + element :new_issue_link, "link_to _('New issue'), new_project_issue_path(@project)" end view 'app/views/shared/_ref_switcher.html.haml' do diff --git a/spec/config/object_store_settings_spec.rb b/spec/config/object_store_settings_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..b1ada3c99db626320a9b5184b0ef6a14155573f6 --- /dev/null +++ b/spec/config/object_store_settings_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' +require Rails.root.join('config', 'object_store_settings.rb') + +describe ObjectStoreSettings do + describe '.parse' do + it 'should set correct default values' do + settings = described_class.parse(nil) + + expect(settings['enabled']).to be false + expect(settings['direct_upload']).to be false + expect(settings['background_upload']).to be true + expect(settings['remote_directory']).to be nil + end + + it 'respects original values' do + original_settings = Settingslogic.new({ + 'enabled' => true, + 'remote_directory' => 'artifacts' + }) + + settings = described_class.parse(original_settings) + + expect(settings['enabled']).to be true + expect(settings['direct_upload']).to be false + expect(settings['background_upload']).to be true + expect(settings['remote_directory']).to eq 'artifacts' + end + end +end diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb index 92addf30307ddd66d3718ccc0decb31d5fbbff0a..fed6677935e803d5b7ef9fe8ba62ce93b8d520df 100644 --- a/spec/controllers/projects/wikis_controller_spec.rb +++ b/spec/controllers/projects/wikis_controller_spec.rb @@ -1,8 +1,35 @@ require 'spec_helper' describe Projects::WikisController do - let(:project) { create(:project_empty_repo, :public) } + let(:project) { create(:project, :public, :repository) } let(:user) { create(:user) } + let(:wiki) { ProjectWiki.new(project, user) } + + describe 'GET #show' do + let(:wiki_title) { 'page-title-test' } + + render_views + + before do + create_page(wiki_title, 'hello world') + end + + it 'limits the retrieved pages for the sidebar' do + sign_in(user) + + expect(controller).to receive(:load_wiki).and_return(wiki) + + # empty? call + expect(wiki).to receive(:pages).with(limit: 1).and_call_original + # Sidebar entries + expect(wiki).to receive(:pages).with(limit: 15).and_call_original + + get :show, namespace_id: project.namespace, project_id: project, id: wiki_title + + expect(response).to have_http_status(:ok) + expect(response.body).to include(wiki_title) + end + end describe 'POST #preview_markdown' do it 'renders json in a correct format' do @@ -13,4 +40,12 @@ describe Projects::WikisController do expect(JSON.parse(response.body).keys).to match_array(%w(body references)) end end + + def create_page(name, content) + project.wiki.wiki.write_page(name, :markdown, content, commit_details(name)) + end + + def commit_details(name) + Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "created page #{name}") + end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index b0acf4a49ac0f602c7e53fc2bc076967a9e389d2..071f96a729efa58057556dd099132c5f6abd5cde 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' describe UsersController do let(:user) { create(:user) } + let(:private_user) { create(:user, private_profile: true) } + let(:public_user) { create(:user) } describe 'GET #show' do context 'with rendered views' do @@ -98,16 +100,47 @@ describe UsersController do expect(assigns(:events)).to be_empty end + + it 'hides events if the user has a private profile' do + Gitlab::DataBuilder::Push.build_sample(project, private_user) + + get :show, username: private_user.username, format: :json + + expect(assigns(:events)).to be_empty + end end end describe 'GET #calendar' do - it 'renders calendar' do - sign_in(user) + context 'for user' do + let(:project) { create(:project) } + + before do + sign_in(user) + project.add_developer(user) + end + + context 'with public profile' do + it 'renders calendar' do + push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user) + EventCreateService.new.push(project, public_user, push_data) + + get :calendar, username: public_user.username, format: :json - get :calendar, username: user.username, format: :json + expect(response).to have_gitlab_http_status(200) + end + end + + context 'with private profile' do + it 'does not render calendar' do + push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user) + EventCreateService.new.push(project, private_user, push_data) - expect(response).to have_gitlab_http_status(200) + get :calendar, username: private_user.username, format: :json + + expect(response).to have_gitlab_http_status(:not_found) + end + end end context 'forked project' do @@ -150,9 +183,26 @@ describe UsersController do expect(assigns(:calendar_date)).to eq(Date.parse('2014-07-31')) end - it 'renders calendar_activities' do - get :calendar_activities, username: user.username - expect(response).to render_template('calendar_activities') + context 'for user' do + context 'with public profile' do + it 'renders calendar_activities' do + push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user) + EventCreateService.new.push(project, public_user, push_data) + + get :calendar_activities, username: public_user.username + expect(assigns[:events]).not_to be_empty + end + end + + context 'with private profile' do + it 'does not render calendar_activities' do + push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user) + EventCreateService.new.push(project, private_user, push_data) + + get :calendar_activities, username: private_user.username + expect(response).to have_gitlab_http_status(:not_found) + end + end end end diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index 845a7c5fc4207b87643825f4d900b30453efc51d..fd8629ae50404ae3252dc1c64a2f1d32794b8fb9 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -49,7 +49,7 @@ describe 'Multiple issue updating from issues#index', :js do click_update_issues_button page.within('.issue .controls') do - expect(find('.author_link')["title"]).to have_content(user.name) + expect(find('.author-link')["title"]).to have_content(user.name) end end @@ -63,7 +63,7 @@ describe 'Multiple issue updating from issues#index', :js do click_link 'Unassigned' click_update_issues_button - expect(find('.issue:first-child .controls')).not_to have_css('.author_link') + expect(find('.issue:first-child .controls')).not_to have_css('.author-link') end end diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb index bb327159cb0f1bf7a58b5d1757fdc5d7446c2878..cb6603d3f50d938ca0f0b525e01c459692a3d81e 100644 --- a/spec/features/merge_requests/user_mass_updates_spec.rb +++ b/spec/features/merge_requests/user_mass_updates_spec.rb @@ -47,7 +47,7 @@ describe 'Merge requests > User mass updates', :js do change_assignee(user.name) page.within('.merge-request .controls') do - expect(find('.author_link')["title"]).to have_content(user.name) + expect(find('.author-link')["title"]).to have_content(user.name) end end end @@ -62,7 +62,7 @@ describe 'Merge requests > User mass updates', :js do it 'removes assignee from the merge request' do change_assignee('Unassigned') - expect(find('.merge-request .controls')).not_to have_css('.author_link') + expect(find('.merge-request .controls')).not_to have_css('.author-link') end end end diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index 23d8d606790573cdea4c2f497701ff938ae159a4..534cfe1eb127b6721882618e75f67f2b10ba5130 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -238,6 +238,5 @@ def check_author_link(email, author) author_link = find('.commit-author-link') expect(author_link['href']).to eq(user_path(author)) - expect(author_link['title']).to eq(email) expect(find('.commit-author-name').text).to eq(author.name) end diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb index 3d05474dca2052b6b1707226d5b23aa014f4d76a..5003eb508c248854f412b93d8d12e505d2d80a94 100644 --- a/spec/features/signed_commits_spec.rb +++ b/spec/features/signed_commits_spec.rb @@ -7,7 +7,7 @@ describe 'GPG signed commits', :js do user = create :user, email: 'unrelated.user@example.org' project.add_maintainer(user) - Sidekiq::Testing.inline! do + perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User1.public_key, user: user end @@ -21,7 +21,7 @@ describe 'GPG signed commits', :js do end # user changes his email which makes the gpg key verified - Sidekiq::Testing.inline! do + perform_enqueued_jobs do user.skip_reconfirmation! user.update!(email: GpgHelpers::User1.emails.first) end @@ -48,7 +48,7 @@ describe 'GPG signed commits', :js do end # user adds the gpg key which makes the signature valid - Sidekiq::Testing.inline! do + perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User1.public_key, user: user end @@ -66,7 +66,7 @@ describe 'GPG signed commits', :js do end let(:user_1_key) do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User1.public_key, user: user_1 end end @@ -79,7 +79,7 @@ describe 'GPG signed commits', :js do end let(:user_2_key) do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User2.public_key, user: user_2 end end diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index 3e2fb704bc6b3d6be257f6eb71a7c91114d59529..207c333c636e624f3edf1c467864f5d8cab49e80 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -3,15 +3,53 @@ require 'spec_helper' describe 'User page' do let(:user) { create(:user) } - it 'shows all the tabs' do - visit(user_path(user)) - - page.within '.nav-links' do - expect(page).to have_link('Activity') - expect(page).to have_link('Groups') - expect(page).to have_link('Contributed projects') - expect(page).to have_link('Personal projects') - expect(page).to have_link('Snippets') + context 'with public profile' do + it 'shows all the tabs' do + visit(user_path(user)) + + page.within '.nav-links' do + expect(page).to have_link('Activity') + expect(page).to have_link('Groups') + expect(page).to have_link('Contributed projects') + expect(page).to have_link('Personal projects') + expect(page).to have_link('Snippets') + end + end + + it 'does not show private profile message' do + visit(user_path(user)) + + expect(page).not_to have_content("This user has a private profile") + end + end + + context 'with private profile' do + let(:user) { create(:user, private_profile: true) } + + it 'shows no tab' do + visit(user_path(user)) + + expect(page).to have_css("div.profile-header") + expect(page).not_to have_css("ul.nav-links") + end + + it 'shows private profile message' do + visit(user_path(user)) + + expect(page).to have_content("This user has a private profile") + end + + it 'shows own tabs' do + sign_in(user) + visit(user_path(user)) + + page.within '.nav-links' do + expect(page).to have_link('Activity') + expect(page).to have_link('Groups') + expect(page).to have_link('Contributed projects') + expect(page).to have_link('Personal projects') + expect(page).to have_link('Snippets') + end end end diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb index da043f940219c42494a4979930fe23cb83bf3562..58470f4c84d73802921e4bbc072d5b325421c24b 100644 --- a/spec/finders/user_recent_events_finder_spec.rb +++ b/spec/finders/user_recent_events_finder_spec.rb @@ -29,11 +29,22 @@ describe UserRecentEventsFinder do public_project.add_developer(current_user) end - it 'returns all the events' do - expect(finder.execute).to include(private_event, internal_event, public_event) + context 'when profile is public' do + it 'returns all the events' do + expect(finder.execute).to include(private_event, internal_event, public_event) + end + end + + context 'when profile is private' do + it 'returns no event' do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, project_owner).and_return(false) + expect(finder.execute).to be_empty + end end it 'does not include the events if the user cannot read cross project' do + expect(Ability).to receive(:allowed?).and_call_original expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false } expect(finder.execute).to be_empty end diff --git a/spec/fixtures/api/schemas/pipeline.json b/spec/fixtures/api/schemas/pipeline.json index 55511d17b5e6dbde955dcf510a17d16ecf601358..b6e30c40f1319b870638f52d9d0d54776265c746 100644 --- a/spec/fixtures/api/schemas/pipeline.json +++ b/spec/fixtures/api/schemas/pipeline.json @@ -319,6 +319,10 @@ "id": "/properties/updated_at", "type": "string" }, + "web_url": { + "id": "/properties/web_url", + "type": "string" + }, "user": { "id": "/properties/user", "properties": { diff --git a/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json b/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json index 0d127dc52973993c1248fcefcedc215e529d2245..56f86856dd4215018ef1c6569911b7a92106b155 100644 --- a/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json +++ b/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json @@ -4,13 +4,15 @@ "id", "sha", "ref", - "status" + "status", + "web_url" ], "properties" : { "id": { "type": "integer" }, "sha": { "type": "string" }, "ref": { "type": "string" }, - "status": { "type": "string" } + "status": { "type": "string" }, + "web_url": { "type": "string" } }, "additionalProperties": false } diff --git a/spec/fixtures/api/schemas/public_api/v4/projects.json b/spec/fixtures/api/schemas/public_api/v4/projects.json index 17ad8d8c48df08f1ecc3bb202efc87c017e4e9d4..af5670ebd334049aad9d95db211ac8926baf7da9 100644 --- a/spec/fixtures/api/schemas/public_api/v4/projects.json +++ b/spec/fixtures/api/schemas/public_api/v4/projects.json @@ -24,13 +24,24 @@ "avatar_url": { "type": ["string", "null"] }, "star_count": { "type": "integer" }, "forks_count": { "type": "integer" }, - "last_activity_at": { "type": "date" } + "last_activity_at": { "type": "date" }, + "namespace": { + "type": "object", + "properties" : { + "id": { "type": "integer" }, + "name": { "type": "string" }, + "path": { "type": "string" }, + "kind": { "type": "string" }, + "full_path": { "type": "string" }, + "parent_id": { "type": ["integer", "null"] } + } + } }, "required": [ "id", "name", "name_with_namespace", "description", "path", "path_with_namespace", "created_at", "default_branch", "tag_list", "ssh_url_to_repo", "http_url_to_repo", "web_url", "avatar_url", - "star_count", "last_activity_at" + "star_count", "last_activity_at", "namespace" ], "additionalProperties": false } diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb index fee8df1012938b41be5f41cdf5db287f6faec1c0..630f3eff2584b43dd4ef10347be5fef3d3dba792 100644 --- a/spec/helpers/button_helper_spec.rb +++ b/spec/helpers/button_helper_spec.rb @@ -121,6 +121,8 @@ describe ButtonHelper do end describe 'clipboard_button' do + include IconsHelper + let(:user) { create(:user) } let(:project) { build_stubbed(:project) } @@ -145,7 +147,7 @@ describe ButtonHelper do expect(element.attr('data-clipboard-text')).to eq(nil) expect(element.inner_text).to eq("") - expect(element).to have_selector('.fa.fa-clipboard') + expect(element.to_html).to include sprite_icon('duplicate') end end @@ -178,7 +180,7 @@ describe ButtonHelper do context 'with `hide_button_icon` attribute provided' do it 'shows copy to clipboard button without tooltip support' do - expect(element(hide_button_icon: true)).not_to have_selector('.fa.fa-clipboard') + expect(element(hide_button_icon: true).to_html).not_to include sprite_icon('duplicate') end end end diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb index b18c045848f568be95c7fe682ef68313a7790bb3..b079802cb8197f2511ce6d04112f1725ea14989a 100644 --- a/spec/helpers/users_helper_spec.rb +++ b/spec/helpers/users_helper_spec.rb @@ -25,8 +25,20 @@ describe UsersHelper do allow(helper).to receive(:can?).and_return(true) end - it 'includes all the expected tabs' do - expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets) + context 'with public profile' do + it 'includes all the expected tabs' do + expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets) + end + end + + context 'with private profile' do + before do + allow(helper).to receive(:can?).with(user, :read_user_profile, nil).and_return(false) + end + + it 'is empty' do + expect(tabs).to be_empty + end end end diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb index 5077c89d7b45eb52662de726e44928740f007888..a3be222b7bdd32180a5f091b24eaf0df8ccb1614 100644 --- a/spec/helpers/visibility_level_helper_spec.rb +++ b/spec/helpers/visibility_level_helper_spec.rb @@ -6,6 +6,29 @@ describe VisibilityLevelHelper do let(:personal_snippet) { build(:personal_snippet) } let(:project_snippet) { build(:project_snippet) } + describe 'visibility_icon_description' do + context 'used with a Project' do + it 'delegates projects to #project_visibility_icon_description' do + expect(visibility_icon_description(project)) + .to match /project/i + end + + context 'used with a ProjectPresenter' do + it 'delegates projects to #project_visibility_icon_description' do + expect(visibility_icon_description(project.present)) + .to match /project/i + end + end + + context 'used with a Group' do + it 'delegates groups to #group_visibility_icon_description' do + expect(visibility_icon_description(group)) + .to match /group/i + end + end + end + end + describe 'visibility_level_description' do context 'used with a Project' do it 'delegates projects to #project_visibility_level_description' do diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js index cb85d12daf253f5b2feba39cdd422b9bf89bd437..bdc94131fc2d66689948589cc6fd05f98886c4cd 100644 --- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js +++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js @@ -50,7 +50,11 @@ describe('DiffLineGutterContent', () => { it('should return discussions for the given lineCode', () => { const { lineCode } = getDiffFileMock().highlightedDiffLines[1]; - const component = createComponent({ lineCode, showCommentButton: true }); + const component = createComponent({ + lineCode, + showCommentButton: true, + discussions: getDiscussionsMockData(), + }); setDiscussions(component); diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js index 97c771dcfd358e93d5d8f8df05a28262f3025b35..78330dd9633deaade097bced27c651b6ee251ae7 100644 --- a/spec/javascripts/gpg_badges_spec.js +++ b/spec/javascripts/gpg_badges_spec.js @@ -1,23 +1,27 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import GpgBadges from '~/gpg_badges'; +import { TEST_HOST } from 'spec/test_constants'; describe('GpgBadges', () => { let mock; const dummyCommitSha = 'n0m0rec0ffee'; const dummyBadgeHtml = 'dummy html'; const dummyResponse = { - signatures: [{ - commit_sha: dummyCommitSha, - html: dummyBadgeHtml, - }], + signatures: [ + { + commit_sha: dummyCommitSha, + html: dummyBadgeHtml, + }, + ], }; + const dummyUrl = `${TEST_HOST}/dummy/signatures`; beforeEach(() => { mock = new MockAdapter(axios); setFixtures(` <form - class="commits-search-form js-signature-container" data-signatures-path="/hello" action="/hello" + class="commits-search-form js-signature-container" data-signatures-path="${dummyUrl}" action="${dummyUrl}" method="get"> <input name="utf8" type="hidden" value="✓"> <input type="search" name="search" id="commits-search"class="form-control search-text-input input-short"> @@ -32,25 +36,55 @@ describe('GpgBadges', () => { mock.restore(); }); - it('displays a loading spinner', (done) => { - mock.onGet('/hello').reply(200); + it('does not make a request if there is no container element', done => { + setFixtures(''); + spyOn(axios, 'get'); - GpgBadges.fetch().then(() => { - expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null); - const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin'); - expect(spinners.length).toBe(1); - done(); - }).catch(done.fail); + GpgBadges.fetch() + .then(() => { + expect(axios.get).not.toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); }); - it('replaces the loading spinner', (done) => { - mock.onGet('/hello').reply(200, dummyResponse); + it('throws an error if the endpoint is missing', done => { + setFixtures('<div class="js-signature-container"></div>'); + spyOn(axios, 'get'); - GpgBadges.fetch().then(() => { - expect(document.querySelector('.js-loading-gpg-badge')).toBe(null); - const parentContainer = document.querySelector('.parent-container'); - expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml); - done(); - }).catch(done.fail); + GpgBadges.fetch() + .then(() => done.fail('Expected error to be thrown')) + .catch(error => { + expect(error.message).toBe('Missing commit signatures endpoint!'); + expect(axios.get).not.toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); + + it('displays a loading spinner', done => { + mock.onGet(dummyUrl).replyOnce(200); + + GpgBadges.fetch() + .then(() => { + expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null); + const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin'); + expect(spinners.length).toBe(1); + done(); + }) + .catch(done.fail); + }); + + it('replaces the loading spinner', done => { + mock.onGet(dummyUrl).replyOnce(200, dummyResponse); + + GpgBadges.fetch() + .then(() => { + expect(document.querySelector('.js-loading-gpg-badge')).toBe(null); + const parentContainer = document.querySelector('.parent-container'); + expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml); + done(); + }) + .catch(done.fail); }); }); diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index eb5e0bddb74e7246aea8f3daee5cfe55112d4703..363283824485e9378a89f886026357ba9f0d4648 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -76,7 +76,7 @@ describe('Issuable output', () => { expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>'); expect(vm.$el.querySelector('.js-task-list-field').value).toContain('this is a description'); expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/); - expect(editedText.querySelector('.author_link').href).toMatch(/\/some_user$/); + expect(editedText.querySelector('.author-link').href).toMatch(/\/some_user$/); expect(editedText.querySelector('time')).toBeTruthy(); }) .then(() => { @@ -90,7 +90,7 @@ describe('Issuable output', () => { expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42'); expect(vm.$el.querySelector('.edited-text')).toBeTruthy(); expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/); - expect(editedText.querySelector('.author_link').href).toMatch(/\/other_user$/); + expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/); expect(editedText.querySelector('time')).toBeTruthy(); }) .then(done) diff --git a/spec/javascripts/issue_show/components/edited_spec.js b/spec/javascripts/issue_show/components/edited_spec.js index 2061def699b45c8ead378eda2dde8484e99eda8d..7f09db837bb238702ffe03e6a269795b9ba2b1be 100644 --- a/spec/javascripts/issue_show/components/edited_spec.js +++ b/spec/javascripts/issue_show/components/edited_spec.js @@ -18,7 +18,7 @@ describe('edited', () => { }).$mount(); expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited[\s\S]+?by Some User/); - expect(editedComponent.$el.querySelector('.author_link').href).toMatch(/\/some_user$/); + expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/); expect(editedComponent.$el.querySelector('time')).toBeTruthy(); }); @@ -31,7 +31,7 @@ describe('edited', () => { }).$mount(); expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited by Some User/); - expect(editedComponent.$el.querySelector('.author_link').href).toMatch(/\/some_user$/); + expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/); expect(editedComponent.$el.querySelector('time')).toBeFalsy(); }); @@ -43,7 +43,7 @@ describe('edited', () => { }).$mount(); expect(formatText(editedComponent.$el.innerText)).not.toMatch(/by Some User/); - expect(editedComponent.$el.querySelector('.author_link')).toBeFalsy(); + expect(editedComponent.$el.querySelector('.author-link')).toBeFalsy(); expect(editedComponent.$el.querySelector('time')).toBeTruthy(); }); diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 41ff59949e56a33896c94cf836c12c465b8cfd0c..71b26a315af065799f6db9da910e716077c38752 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -627,4 +627,23 @@ describe('common_utils', () => { }); }); }); + + describe('roundOffFloat', () => { + it('Rounds off decimal places of a float number with provided precision', () => { + expect(commonUtils.roundOffFloat(3.141592, 3)).toBe(3.142); + }); + + it('Rounds off a float number to a whole number when provided precision is zero', () => { + expect(commonUtils.roundOffFloat(3.141592, 0)).toBe(3); + expect(commonUtils.roundOffFloat(3.5, 0)).toBe(4); + }); + + it('Rounds off float number to nearest 0, 10, 100, 1000 and so on when provided precision is below 0', () => { + expect(commonUtils.roundOffFloat(34567.14159, -1)).toBe(34570); + expect(commonUtils.roundOffFloat(34567.14159, -2)).toBe(34600); + expect(commonUtils.roundOffFloat(34567.14159, -3)).toBe(35000); + expect(commonUtils.roundOffFloat(34567.14159, -4)).toBe(30000); + expect(commonUtils.roundOffFloat(34567.14159, -5)).toBe(0); + }); + }); }); diff --git a/spec/javascripts/reports/store/actions_spec.js b/spec/javascripts/reports/store/actions_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..c714c5af156ee3a3ec7da00c67df545d8d0d3f55 --- /dev/null +++ b/spec/javascripts/reports/store/actions_spec.js @@ -0,0 +1,130 @@ +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import { + setEndpoint, + requestReports, + fetchReports, + stopPolling, + clearEtagPoll, + receiveReportsSuccess, + receiveReportsError, +} from '~/reports/store/actions'; +import state from '~/reports/store/state'; +import * as types from '~/reports/store/mutation_types'; +import testAction from 'spec/helpers/vuex_action_helper'; +import { TEST_HOST } from 'spec/test_constants'; + +describe('Reports Store Actions', () => { + let mockedState; + + beforeEach(() => { + mockedState = state(); + }); + + describe('setEndpoint', () => { + it('should commit SET_ENDPOINT mutation', done => { + testAction( + setEndpoint, + 'endpoint.json', + mockedState, + [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }], + [], + done, + ); + }); + }); + + describe('requestReports', () => { + it('should commit REQUEST_REPORTS mutation', done => { + testAction(requestReports, null, mockedState, [{ type: types.REQUEST_REPORTS }], [], done); + }); + }); + + describe('fetchReports', () => { + let mock; + + beforeEach(() => { + mockedState.endpoint = `${TEST_HOST}/endpoint.json`; + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + stopPolling(); + clearEtagPoll(); + }); + + describe('success', () => { + it('dispatches requestReports and receiveReportsSuccess ', done => { + mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] }); + + testAction( + fetchReports, + null, + mockedState, + [], + [ + { + type: 'requestReports', + }, + { + payload: { summary: {}, suites: [{ name: 'rspec' }] }, + type: 'receiveReportsSuccess', + }, + ], + done, + ); + }); + }); + + describe('error', () => { + beforeEach(() => { + mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500); + }); + + it('dispatches requestReports and receiveReportsError ', done => { + testAction( + fetchReports, + null, + mockedState, + [], + [ + { + type: 'requestReports', + }, + { + type: 'receiveReportsError', + }, + ], + done, + ); + }); + }); + }); + + describe('receiveReportsSuccess', () => { + it('should commit RECEIVE_REPORTS_SUCCESS mutation', done => { + testAction( + receiveReportsSuccess, + { summary: {} }, + mockedState, + [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: { summary: {} } }], + [], + done, + ); + }); + }); + + describe('receiveReportsError', () => { + it('should commit RECEIVE_REPORTS_ERROR mutation', done => { + testAction( + receiveReportsError, + null, + mockedState, + [{ type: types.RECEIVE_REPORTS_ERROR }], + [], + done, + ); + }); + }); +}); diff --git a/spec/javascripts/reports/store/mutations_spec.js b/spec/javascripts/reports/store/mutations_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..3e0b15438c3291e81b7679a62b107e641381eb7e --- /dev/null +++ b/spec/javascripts/reports/store/mutations_spec.js @@ -0,0 +1,101 @@ +import state from '~/reports/store/state'; +import mutations from '~/reports/store/mutations'; +import * as types from '~/reports/store/mutation_types'; + +describe('Reports Store Mutations', () => { + let stateCopy; + + beforeEach(() => { + stateCopy = state(); + }); + + describe('SET_ENDPOINT', () => { + it('should set endpoint', () => { + mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json'); + expect(stateCopy.endpoint).toEqual('endpoint.json'); + }); + }); + + describe('REQUEST_REPORTS', () => { + it('should set isLoading to true', () => { + mutations[types.REQUEST_REPORTS](stateCopy); + expect(stateCopy.isLoading).toEqual(true); + }); + }); + + describe('RECEIVE_REPORTS_SUCCESS', () => { + const mockedResponse = { + summary: { + total: 14, + resolved: 0, + failed: 7, + }, + suites: [ + { + name: 'build:linux', + summary: { + total: 2, + resolved: 0, + failed: 1, + }, + new_failures: [ + { + name: 'StringHelper#concatenate when a is git and b is lab returns summary', + execution_time: 0.0092435, + system_output: + 'Failure/Error: is_expected.to eq(\'gitlab\')', + }, + ], + resolved_failures: [ + { + name: 'StringHelper#concatenate when a is git and b is lab returns summary', + execution_time: 0.009235, + system_output: + 'Failure/Error: is_expected.to eq(\'gitlab\')', + }, + ], + existing_failures: [ + { + name: 'StringHelper#concatenate when a is git and b is lab returns summary', + execution_time: 1232.08, + system_output: + 'Failure/Error: is_expected.to eq(\'gitlab\')', + }, + ], + }, + ], + }; + + beforeEach(() => { + mutations[types.RECEIVE_REPORTS_SUCCESS](stateCopy, mockedResponse); + }); + + it('should reset isLoading', () => { + expect(stateCopy.isLoading).toEqual(false); + }); + + it('should set summary counts', () => { + expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total); + expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved); + expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed); + }); + + it('should set reports', () => { + expect(stateCopy.reports).toEqual(mockedResponse.suites); + }); + }); + + describe('RECEIVE_REPORTS_ERROR', () => { + beforeEach(() => { + mutations[types.RECEIVE_REPORTS_ERROR](stateCopy); + }); + it('should reset isLoading', () => { + expect(stateCopy.isLoading).toEqual(false); + }); + + it('should set hasError to true', () => { + expect(stateCopy.hasError).toEqual(true); + }); + + }); +}); diff --git a/spec/javascripts/sidebar/assignees_spec.js b/spec/javascripts/sidebar/assignees_spec.js index 4e4343812bd44fd04b50229b9943b35a2b75ef32..843e70021801c8119788b2af6b8a1831f104063d 100644 --- a/spec/javascripts/sidebar/assignees_spec.js +++ b/spec/javascripts/sidebar/assignees_spec.js @@ -102,13 +102,13 @@ describe('Assignee component', () => { }, }).$mount(); - expect(component.$el.querySelector('.author_link')).not.toBeNull(); + expect(component.$el.querySelector('.author-link')).not.toBeNull(); // The image - expect(component.$el.querySelector('.author_link img').getAttribute('src')).toEqual(UsersMock.user.avatar); + expect(component.$el.querySelector('.author-link img').getAttribute('src')).toEqual(UsersMock.user.avatar); // Author name - expect(component.$el.querySelector('.author_link .author').innerText.trim()).toEqual(UsersMock.user.name); + expect(component.$el.querySelector('.author-link .author').innerText.trim()).toEqual(UsersMock.user.name); // Username - expect(component.$el.querySelector('.author_link .username').innerText.trim()).toEqual(`@${UsersMock.user.username}`); + expect(component.$el.querySelector('.author-link .username').innerText.trim()).toEqual(`@${UsersMock.user.username}`); }); it('has the root url present in the assigneeUrl method', () => { diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js index 97f0fbb04dbec76e8243a91215274f442910bbf3..e135690349e8d833e3942af961043ea209929599 100644 --- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js +++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js @@ -21,7 +21,7 @@ describe('clipboard button', () => { it('renders a button for clipboard', () => { expect(vm.$el.tagName).toEqual('BUTTON'); expect(vm.$el.getAttribute('data-clipboard-text')).toEqual('copy me'); - expect(vm.$el.querySelector('i').className).toEqual('fa fa-clipboard'); + expect(vm.$el).toHaveSpriteIcon('duplicate'); }); it('should have a tooltip with default values', () => { 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 de3bf667fb3b5b240ad545be060b865bfcd804fe..076d940961d75c5f4a59910626f59841aee6c46d 100644 --- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js +++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js @@ -10,9 +10,9 @@ const createComponent = (config) => { successLabel: 'Synced', failureLabel: 'Failed', neutralLabel: 'Out of sync', - successCount: 10, - failureCount: 5, - totalCount: 20, + successCount: 25, + failureCount: 10, + totalCount: 5000, }, config); return mountComponent(Component, defaultConfig); @@ -32,7 +32,7 @@ describe('StackedProgressBarComponent', () => { describe('computed', () => { describe('neutralCount', () => { it('returns neutralCount based on totalCount, successCount and failureCount', () => { - expect(vm.neutralCount).toBe(5); // 20 - 10 - 5 + expect(vm.neutralCount).toBe(4965); // 5000 - 25 - 10 }); }); }); @@ -40,7 +40,11 @@ describe('StackedProgressBarComponent', () => { describe('methods', () => { describe('getPercent', () => { it('returns percentage from provided count based on `totalCount`', () => { - expect(vm.getPercent(10)).toBe(50); + expect(vm.getPercent(500)).toBe(10); + }); + + it('returns percentage with decimal place from provided count based on `totalCount`', () => { + expect(vm.getPercent(10)).toBe(0.2); }); }); diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb index 41f957c4e0010042195ce69d3059b9a61f72458e..d06c5535309005085ea96aca2599ab43e7aaa75c 100644 --- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb +++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb @@ -7,6 +7,20 @@ describe Banzai::Filter::ImageLazyLoadFilter do %(<img src="#{path}" />) end + def image_with_class(path, class_attr = nil) + %(<img src="#{path}" class="#{class_attr}"/>) + end + + it 'adds a class attribute' do + doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.at_css('img')['class']).to eq 'lazy' + end + + it 'appends to the current class attribute' do + doc = filter(image_with_class('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg', 'test')) + expect(doc.at_css('img')['class']).to eq 'test lazy' + end + it 'transforms the image src to a data-src' do doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('img')['data-src']).to eq '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg' diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index d930c608b1860b66a9c7c672365bd722867fe26c..0b3c23903049bd3277747935c60b2918942a461e 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -54,6 +54,18 @@ describe Banzai::Filter::SanitizationFilter do expect(instance.whitelist[:transformers].size).to eq control_count end + it 'customizes the whitelist only once for different instances' do + instance1 = described_class.new('Foo1') + instance2 = described_class.new('Foo2') + control_count = instance1.whitelist[:transformers].size + + instance1.whitelist + instance2.whitelist + + expect(instance1.whitelist[:transformers].size).to eq control_count + expect(instance2.whitelist[:transformers].size).to eq control_count + end + it 'sanitizes `class` attribute from all elements' do act = %q{<pre class="code highlight white c"><code><span class="k">def</span></code></pre>} exp = %q{<pre><code><span class="k">def</span></code></pre>} diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb index 6eb10497428cceecd2861cb90c5dc43ee91c2a55..f313e67565414a29c1db084efdfe02570feed950 100644 --- a/spec/lib/feature_spec.rb +++ b/spec/lib/feature_spec.rb @@ -39,18 +39,36 @@ describe Feature do end describe '.persisted?' do - it 'returns true for a persisted feature' do - Feature::FlipperFeature.create!(key: 'foo') + context 'when the feature is persisted' do + it 'returns true when feature name is a string' do + Feature::FlipperFeature.create!(key: 'foo') + + feature = double(:feature, name: 'foo') + + expect(described_class.persisted?(feature)).to eq(true) + end + + it 'returns true when feature name is a symbol' do + Feature::FlipperFeature.create!(key: 'foo') - feature = double(:feature, name: 'foo') + feature = double(:feature, name: :foo) - expect(described_class.persisted?(feature)).to eq(true) + expect(described_class.persisted?(feature)).to eq(true) + end end - it 'returns false for a feature that is not persisted' do - feature = double(:feature, name: 'foo') + context 'when the feature is not persisted' do + it 'returns false when feature name is a string' do + feature = double(:feature, name: 'foo') + + expect(described_class.persisted?(feature)).to eq(false) + end - expect(described_class.persisted?(feature)).to eq(false) + it 'returns false when feature name is a symbol' do + feature = double(:feature, name: :bar) + + expect(described_class.persisted?(feature)).to eq(false) + end end end diff --git a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb index 64c994a268fc7991e06c8668bbcd7d1a7e68b173..1969aed51da2f9b817abe8ccc5b9478f6d4151fe 100644 --- a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb +++ b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180619121030 do +describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, :sidekiq, schema: 20180619121030 do describe '#perform' do context 'when diff files can be deleted' do let(:merge_request) { create(:merge_request, :merged) } diff --git a/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb index fb5093b0bd178667c73a8df3b71aa0fbd10c2697..ec8ba0ce1274ad1ec5560431782742f2cb7879c5 100644 --- a/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb +++ b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::BackgroundMigration::ScheduleDiffFilesDeletion, :migration, schema: 20180619121030 do +describe Gitlab::BackgroundMigration::ScheduleDiffFilesDeletion, :migration, :sidekiq, schema: 20180619121030 do describe '#perform' do let(:merge_request_diffs) { table(:merge_request_diffs) } let(:merge_requests) { table(:merge_requests) } diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index 782e4e45a918662ae37056d0cec2dfc0aff03831..7d76519dddd6f44781edd18f8a48b3f51d9c43f7 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -65,6 +65,28 @@ describe Gitlab::Database do end end + describe '.postgresql_9_or_less?' do + it 'returns false when using MySQL' do + allow(described_class).to receive(:postgresql?).and_return(false) + + expect(described_class.postgresql_9_or_less?).to eq(false) + end + + it 'returns true when using PostgreSQL 9.6' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('9.6') + + expect(described_class.postgresql_9_or_less?).to eq(true) + end + + it 'returns false when using PostgreSQL 10 or newer' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('10') + + expect(described_class.postgresql_9_or_less?).to eq(false) + end + end + describe '.join_lateral_supported?' do it 'returns false when using MySQL' do allow(described_class).to receive(:postgresql?).and_return(false) @@ -109,6 +131,70 @@ describe Gitlab::Database do end end + describe '.pg_wal_lsn_diff' do + it 'returns old name when using PostgreSQL 9.6' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('9.6') + + expect(described_class.pg_wal_lsn_diff).to eq('pg_xlog_location_diff') + end + + it 'returns new name when using PostgreSQL 10 or newer' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('10') + + expect(described_class.pg_wal_lsn_diff).to eq('pg_wal_lsn_diff') + end + end + + describe '.pg_current_wal_insert_lsn' do + it 'returns old name when using PostgreSQL 9.6' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('9.6') + + expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_xlog_insert_location') + end + + it 'returns new name when using PostgreSQL 10 or newer' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('10') + + expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_wal_insert_lsn') + end + end + + describe '.pg_last_wal_receive_lsn' do + it 'returns old name when using PostgreSQL 9.6' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('9.6') + + expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_xlog_receive_location') + end + + it 'returns new name when using PostgreSQL 10 or newer' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('10') + + expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_wal_receive_lsn') + end + end + + describe '.pg_last_wal_replay_lsn' do + it 'returns old name when using PostgreSQL 9.6' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('9.6') + + expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_xlog_replay_location') + end + + it 'returns new name when using PostgreSQL 10 or newer' do + allow(described_class).to receive(:postgresql?).and_return(true) + allow(described_class).to receive(:version).and_return('10') + + expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_wal_replay_lsn') + end + end + describe '.nulls_last_order' do context 'when using PostgreSQL' do before do diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb index b63658e1b3bad750108296b3cf476af7f7760d92..c5666e4ec61de63a210134832d92824a4e2e96d1 100644 --- a/spec/lib/gitlab/git/wiki_spec.rb +++ b/spec/lib/gitlab/git/wiki_spec.rb @@ -6,6 +6,31 @@ describe Gitlab::Git::Wiki do let(:project_wiki) { ProjectWiki.new(project, user) } subject { project_wiki.wiki } + describe '#pages' do + before do + create_page('page1', 'content') + create_page('page2', 'content2') + end + + after do + destroy_page('page1') + destroy_page('page2') + end + + it 'returns all the pages' do + expect(subject.pages.count).to eq(2) + expect(subject.pages.first.title).to eq 'page1' + expect(subject.pages.last.title).to eq 'page2' + end + + it 'returns only one page' do + pages = subject.pages(limit: 1) + + expect(pages.count).to eq(1) + expect(pages.first.title).to eq 'page1' + end + end + describe '#page' do before do create_page('page1', 'content') diff --git a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb index 6ad9f5ef76662807d21dedd92f2b37ce6e7648dc..5f67fe6b952625337c6ee6924907ce1c16c12b53 100644 --- a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb @@ -70,6 +70,15 @@ describe Gitlab::GitalyClient::WikiService do subject end + it 'sends a limit of 0 to wiki_get_all_pages' do + expect_any_instance_of(Gitaly::WikiService::Stub) + .to receive(:wiki_get_all_pages) + .with(gitaly_request_with_params(limit: 0), kind_of(Hash)) + .and_return([].each) + + subject + end + it 'concatenates the raw data and returns a pair of WikiPage and WikiPageVersion for each page' do expect_any_instance_of(Gitaly::WikiService::Stub) .to receive(:wiki_get_all_pages) @@ -84,5 +93,18 @@ describe Gitlab::GitalyClient::WikiService do expect(wiki_page_2.raw_data).to eq('cd') expect(wiki_page_2_version.format).to eq('markdown') end + + context 'with limits' do + subject { client.get_all_pages(limit: 1) } + + it 'sends a request with the limit' do + expect_any_instance_of(Gitaly::WikiService::Stub) + .to receive(:wiki_get_all_pages) + .with(gitaly_request_with_params(limit: 1), kind_of(Hash)) + .and_return([].each) + + subject + end + end end end diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb index 50e8d7183ce0ef8abc6a9ba1a6124b2c741e20b3..22ad88e28cbbf50a5e7c1fb2ffc5a5c333793ed8 100644 --- a/spec/lib/gitlab/gitlab_import/client_spec.rb +++ b/spec/lib/gitlab/gitlab_import/client_spec.rb @@ -15,4 +15,88 @@ describe Gitlab::GitlabImport::Client do expect(key).to be_kind_of(Symbol) end end + + it 'uses membership and simple flags' do + stub_request('/api/v4/projects?membership=true&page=1&per_page=100&simple=true') + + expect_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([]) + + expect(client.projects.to_a).to eq [] + end + + shared_examples 'pagination params' do + before do + allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([]) + end + + it 'allows page_limit param' do + allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return(element_list) + + expect(client).to receive(:lazy_page_iterator).with(hash_including(page_limit: 2)).and_call_original + + client.send(method, *args, page_limit: 2, per_page: 1).to_a + end + + it 'allows per_page param' do + expect(client).to receive(:lazy_page_iterator).with(hash_including(per_page: 2)).and_call_original + + client.send(method, *args, per_page: 2).to_a + end + + it 'allows starting_page param' do + expect(client).to receive(:lazy_page_iterator).with(hash_including(starting_page: 3)).and_call_original + + client.send(method, *args, starting_page: 3).to_a + end + end + + describe '#projects' do + subject(:method) { :projects } + let(:args) { [] } + let(:element_list) { build_list(:project, 2) } + + before do + stub_request('/api/v4/projects?membership=true&page=1&per_page=1&simple=true') + stub_request('/api/v4/projects?membership=true&page=2&per_page=1&simple=true') + stub_request('/api/v4/projects?membership=true&page=1&per_page=2&simple=true') + stub_request('/api/v4/projects?membership=true&page=3&per_page=100&simple=true') + end + + it_behaves_like 'pagination params' + end + + describe '#issues' do + subject(:method) { :issues } + let(:args) { [1] } + let(:element_list) { build_list(:issue, 2) } + + before do + stub_request('/api/v4/projects/1/issues?page=1&per_page=1') + stub_request('/api/v4/projects/1/issues?page=2&per_page=1') + stub_request('/api/v4/projects/1/issues?page=1&per_page=2') + stub_request('/api/v4/projects/1/issues?page=3&per_page=100') + end + + it_behaves_like 'pagination params' + end + + describe '#issue_comments' do + subject(:method) { :issue_comments } + let(:args) { [1, 1] } + let(:element_list) { build_list(:note_on_issue, 2) } + + before do + stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=1') + stub_request('/api/v4/projects/1/issues/1/notes?page=2&per_page=1') + stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=2') + stub_request('/api/v4/projects/1/issues/1/notes?page=3&per_page=100') + end + + it_behaves_like 'pagination params' + end + + def stub_request(path) + WebMock.stub_request(:get, "https://gitlab.com#{path}") + .to_return(status: 200) + end end diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb index 813ae43b4d3288f5a7eba7ffb0ba5679889a9ec7..7eac2cacb90aaa392cc5650f2f64d024019d6e8b 100644 --- a/spec/lib/gitlab/hashed_storage/migrator_spec.rb +++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb @@ -65,7 +65,7 @@ describe Gitlab::HashedStorage::Migrator do end it 'migrate project' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do subject.migrate(project) end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 084ce3066d6901876ca1a606a8cc36fa5f637099..db5aab0cd76e24bd063b0ef51e624fa65a020017 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -211,6 +211,7 @@ project: - slack_service - microsoft_teams_service - mattermost_service +- hangouts_chat_service - buildkite_service - bamboo_service - teamcity_service diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 20def4fefe2166462890e21b027e2a04ceca8358..a19b3c0ba666021f0d588b5e71a9dcd157d05a58 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -133,7 +133,7 @@ describe Gitlab::UsageData do expect(subject[:signup_enabled]).to eq(Gitlab::CurrentSettings.allow_signup?) expect(subject[:ldap_enabled]).to eq(Gitlab.config.ldap.enabled) expect(subject[:gravatar_enabled]).to eq(Gitlab::CurrentSettings.gravatar_enabled?) - expect(subject[:omniauth_enabled]).to eq(Gitlab.config.omniauth.enabled) + expect(subject[:omniauth_enabled]).to eq(Gitlab::Auth.omniauth_enabled?) expect(subject[:reply_by_email_enabled]).to eq(Gitlab::IncomingEmail.enabled?) expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled) expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled) diff --git a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb index 4395e2f82641a377cb1f335d87f7684a42e27249..5c6f213e15bbbcf54975a64cbca99fb4e5acb12a 100644 --- a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb +++ b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb @@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnServices, :migration, :sidekiq do end it 'correctly processes services' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(services_table.where(confidential_note_events: nil).count).to eq 4 expect(services_table.where(confidential_note_events: true).count).to eq 1 diff --git a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb index a837498e1b1446c63b4810f95aa29f2490815df6..dd6f53257506aacf4044c21b74c7ee74671b22c7 100644 --- a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb +++ b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb @@ -44,7 +44,7 @@ describe MigrateStageIdReferenceInBackground, :migration, :sidekiq do end it 'schedules background migrations' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(jobs.where(stage_id: nil).count).to eq 5 migrate! diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb index ce35276cbf5bb8812c69fb8660b3c0d6c263a4bc..5483e24fce7c6570029ac59c40bb9a958d22f36d 100644 --- a/spec/migrations/migrate_stages_statuses_spec.rb +++ b/spec/migrations/migrate_stages_statuses_spec.rb @@ -34,7 +34,7 @@ describe MigrateStagesStatuses, :sidekiq, :migration do end it 'correctly migrates stages statuses' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(stages.where(status: nil).count).to eq 3 migrate! diff --git a/spec/migrations/normalize_ldap_extern_uids_spec.rb b/spec/migrations/normalize_ldap_extern_uids_spec.rb index 56a78f52802f2795c65e5fb3a7a30d91f134a474..c6ea1e3e49e19a2e3a1ea135fadf0628e9e6ea53 100644 --- a/spec/migrations/normalize_ldap_extern_uids_spec.rb +++ b/spec/migrations/normalize_ldap_extern_uids_spec.rb @@ -38,7 +38,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do end it 'migrates the LDAP identities' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do migrate! identities.where(id: 1..4).each do |identity| expect(identity.extern_uid).to eq("uid=foo #{identity.id},ou=people,dc=example,dc=com") @@ -47,7 +47,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do end it 'does not modify non-LDAP identities' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do migrate! identity = identities.last expect(identity.extern_uid).to eq(" uid = foo 5, ou = People, dc = example, dc = com ") diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb index ed306fb3d62c0ec05c500f8613fe9e1742750879..96bef1075990d1a2b7abc71ef295f4a4a764761e 100644 --- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb +++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb @@ -20,7 +20,7 @@ describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do end it 'schedules background migrations' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(GpgKeySubkey.count).to eq(0) migrate! diff --git a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb index d230f06444424e838201c21c5febf06736682834..9f7e47bae0dda2168bf0cca54a71995a295835f4 100644 --- a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb +++ b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb @@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrations, :migration, :sidekiq do end it 'schedules background migrations' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL' expect(merge_request_diffs.where(non_empty).count).to eq 3 diff --git a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb index 1aab4ae165084dce9555cf02126c52717f4469aa..5bcb923af7befbf98c2aea10785302ffb269b996 100644 --- a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb +++ b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb @@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrationsTakeTwo, :migration, :sidekiq do end it 'migrates the data' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL' expect(merge_request_diffs.where(non_empty).count).to eq 3 diff --git a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb index c9fdbe95d13b7e5e43069fcef5dc61e2271aa992..76fe16581ac8c5a9b97a6ca550f74bf3683a0c69 100644 --- a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb +++ b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb @@ -53,7 +53,7 @@ describe ScheduleMergeRequestLatestMergeRequestDiffIdMigrations, :migration, :si end it 'schedules background migrations' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(merge_requests_table.where(latest_merge_request_diff_id: nil).count).to eq 3 migrate! diff --git a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb index 027f4a91c90c107d2721692b35f9b9b032d37705..fa4ddd5fbc7220e576263900ccf78d84da6458ee 100644 --- a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb +++ b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb @@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnWebhooks, :migration, :sidekiq do end it 'correctly processes web hooks' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(web_hooks_table.where(confidential_note_events: nil).count).to eq 4 expect(web_hooks_table.where(confidential_note_events: true).count).to eq 1 diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 774a638b430bed62da15678ed90da577f7e1f4e1..915bf134d579b83ebaf60c8276c0bb0a45aa87c3 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -179,7 +179,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end it 'migrates data to object storage' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do subject build_trace_chunk.reload @@ -201,7 +201,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end it 'does not migrate data to object storage' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do data_store = build_trace_chunk.data_store subject diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb index f8d51a9583378126711b23658958c36964956464..cd84a684fec3af1207498de814656240efa313dd 100644 --- a/spec/models/deploy_token_spec.rb +++ b/spec/models/deploy_token_spec.rb @@ -62,11 +62,18 @@ describe DeployToken do end end - context "when it hasn't been revoked" do + context "when it hasn't been revoked and is not expired" do it 'should return true' do expect(deploy_token.active?).to be_truthy end end + + context "when it hasn't been revoked and is expired" do + it 'should return true' do + deploy_token.update_attribute(:expires_at, Date.today - 5.days) + expect(deploy_token.active?).to be_falsy + end + end end describe '#username' do diff --git a/spec/models/project_services/hangouts_chat_service_spec.rb b/spec/models/project_services/hangouts_chat_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..cfa55188a647a247bda9b5d476561fdeb8ba14be --- /dev/null +++ b/spec/models/project_services/hangouts_chat_service_spec.rb @@ -0,0 +1,246 @@ +require 'spec_helper' + +describe HangoutsChatService do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before do + subject.active = true + end + + it { is_expected.to validate_presence_of(:webhook) } + it_behaves_like 'issue tracker service URL attribute', :webhook + end + + context 'when service is inactive' do + before do + subject.active = false + end + + it { is_expected.not_to validate_presence_of(:webhook) } + end + end + + describe '#execute' do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:webhook_url) { 'https://example.gitlab.com/' } + + before do + allow(subject).to receive_messages( + project: project, + project_id: project.id, + service_hook: true, + webhook: webhook_url + ) + + WebMock.stub_request(:post, webhook_url) + end + + shared_examples 'Hangouts Chat service' do + it 'calls Hangouts Chat API' do + subject.execute(sample_data) + + expect(WebMock) + .to have_requested(:post, webhook_url) + .with { |req| req.body =~ /\A{"text":.+}\Z/ } + .once + end + end + + context 'with push events' do + let(:sample_data) do + Gitlab::DataBuilder::Push.build_sample(project, user) + end + + it_behaves_like 'Hangouts Chat service' + + it 'specifies the webhook when it is configured' do + expect(HangoutsChat::Sender).to receive(:new).with(webhook_url).and_return(double(:hangouts_chat_service).as_null_object) + + subject.execute(sample_data) + end + + context 'with not default branch' do + let(:sample_data) do + Gitlab::DataBuilder::Push.build(project, user, nil, nil, 'not-the-default-branch') + end + + context 'when notify_only_default_branch enabled' do + before do + subject.notify_only_default_branch = true + end + + it 'does not call the Hangouts Chat API' do + result = subject.execute(sample_data) + + expect(result).to be_falsy + end + end + + context 'when notify_only_default_branch disabled' do + before do + subject.notify_only_default_branch = false + end + + it_behaves_like 'Hangouts Chat service' + end + end + end + + context 'with issue events' do + let(:opts) { { title: 'Awesome issue', description: 'please fix' } } + let(:sample_data) do + service = Issues::CreateService.new(project, user, opts) + issue = service.execute + service.hook_data(issue, 'open') + end + + it_behaves_like 'Hangouts Chat service' + end + + context 'with merge events' do + let(:opts) do + { + title: 'Awesome merge_request', + description: 'please fix', + source_branch: 'feature', + target_branch: 'master' + } + end + + let(:sample_data) do + service = MergeRequests::CreateService.new(project, user, opts) + merge_request = service.execute + service.hook_data(merge_request, 'open') + end + + before do + project.add_developer(user) + end + + it_behaves_like 'Hangouts Chat service' + end + + context 'with wiki page events' do + let(:opts) do + { + title: 'Awesome wiki_page', + content: 'Some text describing some thing or another', + format: 'md', + message: 'user created page: Awesome wiki_page' + } + end + let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) } + let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') } + + it_behaves_like 'Hangouts Chat service' + end + + context 'with note events' do + let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) } + + context 'with commit comment' do + let(:note) do + create(:note_on_commit, author: user, + project: project, + commit_id: project.repository.commit.id, + note: 'a comment on a commit') + end + + it_behaves_like 'Hangouts Chat service' + end + + context 'with merge request comment' do + let(:note) do + create(:note_on_merge_request, project: project, + note: 'merge request note') + end + + it_behaves_like 'Hangouts Chat service' + end + + context 'with issue comment' do + let(:note) do + create(:note_on_issue, project: project, note: 'issue note') + end + + it_behaves_like 'Hangouts Chat service' + end + + context 'with snippet comment' do + let(:note) do + create(:note_on_project_snippet, project: project, + note: 'snippet note') + end + + it_behaves_like 'Hangouts Chat service' + end + end + + context 'with pipeline events' do + let(:pipeline) do + create(:ci_pipeline, + project: project, status: status, + sha: project.commit.sha, ref: project.default_branch) + end + let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) } + + context 'with failed pipeline' do + let(:status) { 'failed' } + + it_behaves_like 'Hangouts Chat service' + end + + context 'with succeeded pipeline' do + let(:status) { 'success' } + + context 'with default notify_only_broken_pipelines' do + it 'does not call Hangouts Chat API' do + result = subject.execute(sample_data) + + expect(result).to be_falsy + end + end + + context 'when notify_only_broken_pipelines is false' do + before do + subject.notify_only_broken_pipelines = false + end + + it_behaves_like 'Hangouts Chat service' + end + end + + context 'with not default branch' do + let(:pipeline) do + create(:ci_pipeline, project: project, status: 'failed', ref: 'not-the-default-branch') + end + + context 'when notify_only_default_branch enabled' do + before do + subject.notify_only_default_branch = true + end + + it 'does not call the Hangouts Chat API' do + result = subject.execute(sample_data) + + expect(result).to be_falsy + end + end + + context 'when notify_only_default_branch disabled' do + before do + subject.notify_only_default_branch = false + end + + it_behaves_like 'Hangouts Chat service' + end + end + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index d200e5f2e4282f64369a4dd63380a6a788cff493..b0ec725bf70f42d6368fd74ad39d597b7afe73c3 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -26,6 +26,7 @@ describe Project do it { is_expected.to have_one(:slack_service) } it { is_expected.to have_one(:microsoft_teams_service) } it { is_expected.to have_one(:mattermost_service) } + it { is_expected.to have_one(:hangouts_chat_service) } it { is_expected.to have_one(:packagist_service) } it { is_expected.to have_one(:pushover_service) } it { is_expected.to have_one(:asana_service) } diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb index 0d6b4384adaa216e7b958520b2f08ed82699c7ac..90a2caaeb88e83f1176b287475aad977fc41e94f 100644 --- a/spec/models/spam_log_spec.rb +++ b/spec/models/spam_log_spec.rb @@ -22,7 +22,7 @@ describe SpamLog do spam_log = build(:spam_log) user = spam_log.user - Sidekiq::Testing.inline! do + perform_enqueued_jobs do spam_log.remove_user(deleted_by: admin) end diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb index bf93555b0f0d9d162fe66dbfccc1cc3c5501c76e..f3db0c122a0a403372cd13674498f626d7d6da47 100644 --- a/spec/requests/api/environments_spec.rb +++ b/spec/requests/api/environments_spec.rb @@ -20,7 +20,7 @@ describe API::Environments do path path_with_namespace star_count forks_count created_at last_activity_at - avatar_url + avatar_url namespace ) get api("/projects/#{project.id}/environments", user) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 65b387a21707e1be0f3fbb216760bc9188d511bf..3a8948f84770193bafa3078c70789526b2e056af 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -226,6 +226,25 @@ describe API::Groups do expect(json_response.first['name']).to eq(group2.name) end end + + context 'when using min_access_level in the request' do + let!(:group3) { create(:group, :private) } + let(:response_groups) { json_response.map { |group| group['id'] } } + + before do + group1.add_developer(user2) + group3.add_master(user2) + end + + it 'returns an array of groups the user has at least master access' do + get api('/groups', user2), min_access_level: 40 + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(response_groups).to eq([group2.id, group3.id]) + end + end end describe "GET /groups/:id" do diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index 7d1a5c1280549fe997229ee4448894b843620ffa..8412d0383f731e1b414f4a7bb1abd10aa5990954 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -220,6 +220,7 @@ describe API::Jobs do expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at) expect(Time.parse(json_response['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at) expect(json_response['duration']).to eq(job.duration) + expect(json_response['web_url']).to be_present end it 'returns pipeline data' do diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index e2ca27f5d41cca0df473306df965e6ad7e79e8e6..342a97b6a69eaf68acb8617928ca412a3b72edbe 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -24,7 +24,8 @@ describe API::Pipelines do expect(json_response).to be_an Array expect(json_response.first['sha']).to match /\A\h{40}\z/ expect(json_response.first['id']).to eq pipeline.id - expect(json_response.first.keys).to contain_exactly(*%w[id sha ref status]) + expect(json_response.first['web_url']).to be_present + expect(json_response.first.keys).to contain_exactly(*%w[id sha ref status web_url]) end context 'when parameter is passed' do diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 41243854ebc06dd1b860d42e5984b39b67e6b8f6..55332f56508444cb47cce68728f86ed0bbae6294 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -102,7 +102,7 @@ describe API::ProjectImport do it 'correctly overrides params during the import' do override_params = { 'description' => 'Hello world' } - Sidekiq::Testing.inline! do + perform_enqueued_jobs do post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index f72c01561d8f6c88d4812207448894cb3e266199..71e3436fa76046885edec03d3539660d5ba80af3 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -225,7 +225,7 @@ describe API::Projects do path path_with_namespace star_count forks_count created_at last_activity_at - avatar_url + avatar_url namespace ) get api('/projects?simple=true', user) @@ -400,6 +400,22 @@ describe API::Projects do end end end + + context 'and with min_access_level' do + before do + project2.add_master(user2) + project3.add_developer(user2) + project4.add_reporter(user2) + end + + it 'returns an array of groups the user has at least developer access' do + get api('/projects', user2), { min_access_level: 30 } + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(project2.id, project3.id) + end + end end context 'when authenticated as a different user' do @@ -681,6 +697,20 @@ describe API::Projects do expect(json_response).to be_an Array expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id) end + + it 'returns projects filetered by minimal access level' do + private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace) + private_project2 = create(:project, :private, name: 'private_project2', creator_id: user4.id, namespace: user4.namespace) + private_project1.add_developer(user2) + private_project2.add_reporter(user2) + + get api("/users/#{user4.id}/projects/", user2), { min_access_level: 30 } + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(private_project1.id) + end end describe 'POST /projects/user/:id' do diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index a97c3f3461a584a98040eaf43be31cc5ad8a98f3..6a051f865aa6df1b34d27bedd201534d4d659860 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -11,6 +11,7 @@ describe API::Users do let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') } let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 } let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 } + let(:private_user) { create(:user, private_profile: true) } describe 'GET /users' do context "when unauthenticated" do @@ -254,6 +255,13 @@ describe API::Users do expect(response).to match_response_schema('public_api/v4/user/admin') expect(json_response['is_admin']).to be(false) end + + it "includes the `created_at` field for private users" do + get api("/users/#{private_user.id}", admin) + + expect(response).to match_response_schema('public_api/v4/user/admin') + expect(json_response.keys).to include 'created_at' + end end context 'for an anonymous user' do @@ -272,6 +280,20 @@ describe API::Users do expect(response).to have_gitlab_http_status(404) end + + it "returns the `created_at` field for public users" do + get api("/users/#{user.id}") + + expect(response).to match_response_schema('public_api/v4/user/basic') + expect(json_response.keys).to include 'created_at' + end + + it "does not return the `created_at` field for private users" do + get api("/users/#{private_user.id}") + + expect(response).to match_response_schema('public_api/v4/user/basic') + expect(json_response.keys).not_to include 'created_at' + end end it "returns a 404 error if user id not found" do @@ -374,6 +396,18 @@ describe API::Users do expect(new_user.recently_sent_password_reset?).to eq(true) end + it "creates user with private profile" do + post api('/users', admin), attributes_for(:user, private_profile: true) + + expect(response).to have_gitlab_http_status(201) + + user_id = json_response['id'] + new_user = User.find(user_id) + + expect(new_user).not_to eq(nil) + expect(new_user.private_profile?).to eq(true) + end + it "does not create user with invalid email" do post api('/users', admin), email: 'invalid email', @@ -583,6 +617,13 @@ describe API::Users do expect(user.reload.external?).to be_truthy end + it "updates private profile" do + put api("/users/#{user.id}", admin), { private_profile: true } + + expect(response).to have_gitlab_http_status(200) + expect(user.reload.private_profile).to eq(true) + end + it "does not update admin status" do put api("/users/#{admin_user.id}", admin), { can_create_group: false } @@ -1067,7 +1108,7 @@ describe API::Users do end it "deletes user" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) } + perform_enqueued_jobs { delete api("/users/#{user.id}", admin) } expect(response).to have_gitlab_http_status(204) expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound @@ -1079,30 +1120,30 @@ describe API::Users do end it "does not delete for unauthenticated user" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}") } + perform_enqueued_jobs { delete api("/users/#{user.id}") } expect(response).to have_gitlab_http_status(401) end it "is not available for non admin users" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}", user) } + perform_enqueued_jobs { delete api("/users/#{user.id}", user) } expect(response).to have_gitlab_http_status(403) end it "returns 404 for non-existing user" do - Sidekiq::Testing.inline! { delete api("/users/999999", admin) } + perform_enqueued_jobs { delete api("/users/999999", admin) } expect(response).to have_gitlab_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end it "returns a 404 for invalid ID" do - Sidekiq::Testing.inline! { delete api("/users/ASDF", admin) } + perform_enqueued_jobs { delete api("/users/ASDF", admin) } expect(response).to have_gitlab_http_status(404) end context "hard delete disabled" do it "moves contributions to the ghost user" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) } + perform_enqueued_jobs { delete api("/users/#{user.id}", admin) } expect(response).to have_gitlab_http_status(204) expect(issue.reload).to be_persisted @@ -1112,7 +1153,7 @@ describe API::Users do context "hard delete enabled" do it "removes contributions" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}?hard_delete=true", admin) } + perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) } expect(response).to have_gitlab_http_status(204) expect(Issue.exists?(issue.id)).to be_falsy diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 0f3e7157e14ccace478ccc9c800007a3bd0b7017..c71eae9164ac5c903fe6afafdf2b141c843d9d80 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -381,6 +381,10 @@ describe 'Git HTTP requests' do context "when authentication fails" do context "when the user is IP banned" do + before do + Gitlab.config.rack_attack.git_basic_auth['enabled'] = true + end + it "responds with status 401" do expect(Rack::Attack::Allow2Ban).to receive(:filter).and_return(true) allow_any_instance_of(Rack::Request).to receive(:ip).and_return('1.2.3.4') @@ -420,6 +424,10 @@ describe 'Git HTTP requests' do end context "when the user isn't blocked" do + before do + Gitlab.config.rack_attack.git_basic_auth['enabled'] = true + end + it "resets the IP in Rack Attack on download" do expect(Rack::Attack::Allow2Ban).to receive(:reset).twice diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index de39abdb746d26fb0fb8dc7749825c358a6925ef..c2378646f8955525f335479a105daeedf43ae903 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -575,6 +575,40 @@ describe 'Git LFS API and storage' do end end + context 'when using Deploy Tokens' do + let(:project) { create(:project, :repository) } + let(:authorization) { authorize_deploy_token } + let(:update_user_permissions) { nil } + let(:role) { nil } + let(:update_lfs_permissions) do + project.lfs_objects << lfs_object + end + + context 'when Deploy Token is valid' do + let(:deploy_token) { create(:deploy_token, projects: [project]) } + + it_behaves_like 'an authorized requests' + end + + context 'when Deploy Token is not valid' do + let(:deploy_token) { create(:deploy_token, projects: [project], read_repository: false) } + + it 'responds with access denied' do + expect(response).to have_gitlab_http_status(401) + end + end + + context 'when Deploy Token is not related to the project' do + let(:another_project) { create(:project, :repository) } + let(:deploy_token) { create(:deploy_token, projects: [another_project]) } + + it 'responds with access forbidden' do + # We render 404, to prevent data leakage about existence of the project + expect(response).to have_gitlab_http_status(404) + end + end + end + context 'when build is authorized as' do let(:authorization) { authorize_ci_project } @@ -1381,6 +1415,10 @@ describe 'Git LFS API and storage' do ActionController::HttpAuthentication::Basic.encode_credentials(user.username, Gitlab::LfsToken.new(user).token) end + def authorize_deploy_token + ActionController::HttpAuthentication::Basic.encode_credentials(deploy_token.username, deploy_token.token) + end + def post_lfs_json(url, body = nil, headers = nil) post(url, body.try(:to_json), (headers || {}).merge('Content-Type' => LfsRequest::CONTENT_TYPE)) end diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb index a9baccd061a1a2acaf2487fd880a70b0aced3dc0..b54491cf5f9c98e5ab22266517b0fa05f1036a54 100644 --- a/spec/services/groups/destroy_service_spec.rb +++ b/spec/services/groups/destroy_service_spec.rb @@ -49,7 +49,7 @@ describe Groups::DestroyService do context 'Sidekiq inline' do before do # Run sidekiq immediately to check that renamed dir will be removed - Sidekiq::Testing.inline! { destroy_group(group, user, async) } + perform_enqueued_jobs { destroy_group(group, user, async) } end it 'verifies that paths have been deleted' do diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb index 9aa9237d875801ae0502a266e36f00cccc5fd10e..a43da01f37e44ce066330522a9876bfe6c01dbe4 100644 --- a/spec/services/projects/create_from_template_service_spec.rb +++ b/spec/services/projects/create_from_template_service_spec.rb @@ -28,7 +28,7 @@ describe Projects::CreateFromTemplateService do context 'the result project' do before do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do @project = subject.execute end diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 38660ad7a0133283ef051274d975501cdbda43ce..e428808ab686f48eecf57cfe4b776102c680a02c 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -45,18 +45,18 @@ describe Projects::DestroyService do shared_examples 'handles errors thrown during async destroy' do |error_message| it 'does not allow the error to bubble up' do expect do - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } end.not_to raise_error end it 'unmarks the project as "pending deletion"' do - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } expect(project.reload.pending_delete).to be(false) end it 'stores an error message in `projects.delete_error`' do - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } expect(project.reload.delete_error).to be_present expect(project.delete_error).to include(error_message) @@ -66,7 +66,7 @@ describe Projects::DestroyService do context 'Sidekiq inline' do before do # Run sidekiq immediatly to check that renamed repository will be removed - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } end context 'when has remote mirrors' do @@ -110,7 +110,7 @@ describe Projects::DestroyService do end it 'keeps project team intact upon an error' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do begin destroy_project(project, user, {}) rescue ::Redis::CannotConnectError @@ -128,7 +128,7 @@ describe Projects::DestroyService do before do project.project_feature.update_attribute("issues_access_level", ProjectFeature::PRIVATE) # Run sidekiq immediately to check that renamed repository will be removed - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } end it_behaves_like 'deleting the project' @@ -172,7 +172,7 @@ describe Projects::DestroyService do it 'allows error to bubble up and rolls back project deletion' do expect do - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } end.to raise_error(Exception, 'Other error message') expect(project.reload.pending_delete).to be(false) diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb index 1cf373d1d72450b777612028cf88560f54d68965..18ecef1c0a18e7420928cfe42c3e144fc9500edf 100644 --- a/spec/services/projects/housekeeping_service_spec.rb +++ b/spec/services/projects/housekeeping_service_spec.rb @@ -35,7 +35,7 @@ describe Projects::HousekeepingService do allow(subject).to receive(:gc_period).and_return(1) project.increment_pushes_since_gc - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect { subject.execute }.to change { project.pushes_since_gc }.to(0) end end diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb index b381504579223f1cc39769f31be7b0507c30d029..e2a600d12d1b3b2126244b0816536e216e8e80cb 100644 --- a/spec/services/projects/import_service_spec.rb +++ b/spec/services/projects/import_service_spec.rb @@ -69,7 +69,7 @@ describe Projects::ImportService do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The repository could not be created." + expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - The repository could not be created." end context 'when repository creation succeeds' do @@ -141,7 +141,7 @@ describe Projects::ImportService do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository" + expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - Failed to import the repository" end context 'when repository import scheduled' do diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb index f82d4b483e7cb55447acdcd9899bd3b5575d2c58..3bae8bfbd42430e8c41b233b368a2af269d3bbd9 100644 --- a/spec/services/users/destroy_service_spec.rb +++ b/spec/services/users/destroy_service_spec.rb @@ -173,7 +173,7 @@ describe Users::DestroyService do describe "user personal's repository removal" do before do - Sidekiq::Testing.inline! { service.execute(user) } + perform_enqueued_jobs { service.execute(user) } end context 'legacy storage' do diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb index 815432aacce7295fac8d22d07035fa6e131d3426..808084c8f7c4e16e61e2d43f6507c98627b8520e 100644 --- a/spec/workers/storage_migrator_worker_spec.rb +++ b/spec/workers/storage_migrator_worker_spec.rb @@ -13,7 +13,7 @@ describe StorageMigratorWorker do end it 'migrates projects in the specified range' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do worker.perform(ids.min, ids.max) end