diff options
190 files changed, 1687 insertions, 663 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d969164cfe..d32b6989388 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 9.5.1 (2017-08-23) + +- [FIXED] Fix merge request pipeline status when pipeline has errors. !13664 +- [FIXED] Commit rows would occasionally render with the wrong language. +- [FIXED] Fix caching of future broadcast messages. +- [FIXED] Only require Sidekiq throttling library when enabled, to reduce cache misses. +- Raise Housekeeping timeout to 24 hours. !13719 + ## 9.5.0 (2017-08-22) - [FIXED] Fix timeouts when creating projects in groups with many members. !13508 @@ -396,12 +396,12 @@ gem 'net-ssh', '~> 4.1.0' # Required for ED25519 SSH host key support group :ed25519 do gem 'rbnacl-libsodium' - gem 'rbnacl', '~> 3.2' + gem 'rbnacl', '~> 4.0' gem 'bcrypt_pbkdf', '~> 1.0' end # Gitaly GRPC client -gem 'gitaly', '~> 0.29.0' +gem 'gitaly', '~> 0.30.0' gem 'toml-rb', '~> 0.3.15', require: false diff --git a/Gemfile.lock b/Gemfile.lock index ec8349cd1df..7d3c53ee010 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -203,7 +203,7 @@ GEM multi_json fast_gettext (1.4.0) ffaker (2.4.0) - ffi (1.9.10) + ffi (1.9.18) flay (2.8.1) erubis (~> 2.7.0) path_expander (~> 1.0) @@ -275,7 +275,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly (0.29.0) + gitaly (0.30.0) google-protobuf (~> 3.1) grpc (~> 1.0) github-linguist (4.7.6) @@ -682,7 +682,7 @@ GEM rake (12.0.0) rblineprof (0.3.6) debugger-ruby_core_source (~> 1.3) - rbnacl (3.4.0) + rbnacl (4.0.2) ffi rbnacl-libsodium (1.0.11) rbnacl (>= 3.0.1) @@ -1019,7 +1019,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.2.0) - gitaly (~> 0.29.0) + gitaly (~> 0.30.0) github-linguist (~> 4.7.0) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.5.1) @@ -1107,7 +1107,7 @@ DEPENDENCIES rainbow (~> 2.2) raindrops (~> 0.18) rblineprof (~> 0.3.6) - rbnacl (~> 3.2) + rbnacl (~> 4.0) rbnacl-libsodium rdoc (~> 4.2) re2 (~> 1.1.1) diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 56f91e95bb9..4319bfcc57f 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -96,18 +96,17 @@ const Api = { .done(projects => callback(projects)); }, - commitMultiple(id, data, callback) { + commitMultiple(id, data) { + // see https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions const url = Api.buildUrl(Api.commitPath) .replace(':id', id); - return $.ajax({ + return this.wrapAjaxCall({ url, type: 'POST', contentType: 'application/json; charset=utf-8', data: JSON.stringify(data), dataType: 'json', - }) - .done(commitData => callback(commitData)) - .fail(message => callback(message.responseJSON)); + }); }, // Return text for a specific license diff --git a/app/assets/javascripts/repo/components/repo_commit_section.vue b/app/assets/javascripts/repo/components/repo_commit_section.vue index 5ec4a9b6593..1282828b504 100644 --- a/app/assets/javascripts/repo/components/repo_commit_section.vue +++ b/app/assets/javascripts/repo/components/repo_commit_section.vue @@ -42,7 +42,9 @@ export default { actions, }; Store.submitCommitsLoading = true; - Service.commitFiles(payload, this.resetCommitState); + Service.commitFiles(payload) + .then(this.resetCommitState) + .catch(() => Flash('An error occured while committing your changes')); }, resetCommitState() { diff --git a/app/assets/javascripts/repo/services/repo_service.js b/app/assets/javascripts/repo/services/repo_service.js index 3cf204e6ec8..af83497fa39 100644 --- a/app/assets/javascripts/repo/services/repo_service.js +++ b/app/assets/javascripts/repo/services/repo_service.js @@ -65,15 +65,17 @@ const RepoService = { return urlArray.join('/'); }, - commitFiles(payload, cb) { - Api.commitMultiple(Store.projectId, payload, (data) => { - if (data.short_id && data.stats) { - Flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice'); - } else { - Flash(data.message); - } - cb(); - }); + commitFiles(payload) { + return Api.commitMultiple(Store.projectId, payload) + .then(this.commitFlash); + }, + + commitFlash(data) { + if (data.short_id && data.stats) { + window.Flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice'); + } else { + window.Flash(data.message); + } }, }; diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index 486d88efbc5..bdcbd4021b3 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -78,7 +78,7 @@ &.s60 { font-size: 32px; line-height: 58px; } &.s70 { font-size: 34px; line-height: 70px; } &.s90 { font-size: 36px; line-height: 88px; } - &.s110 { font-size: 40px; line-height: 108px; font-weight: 300; } + &.s110 { font-size: 40px; line-height: 108px; font-weight: $gl-font-weight-normal; } &.s140 { font-size: 72px; line-height: 138px; } &.s160 { font-size: 96px; line-height: 158px; } } diff --git a/app/assets/stylesheets/framework/badges.scss b/app/assets/stylesheets/framework/badges.scss index 47a8f44c709..6bbe32df772 100644 --- a/app/assets/stylesheets/framework/badges.scss +++ b/app/assets/stylesheets/framework/badges.scss @@ -1,5 +1,5 @@ .badge { - font-weight: normal; + font-weight: $gl-font-weight-normal; background-color: $badge-bg; color: $badge-color; vertical-align: baseline; diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 95a08c960ea..b575ec9de18 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -8,7 +8,7 @@ text-align: center; padding: 20px; color: $gl-text-color; - font-weight: normal; + font-weight: $gl-font-weight-normal; font-size: 14px; line-height: 36px; @@ -213,7 +213,7 @@ h1 { display: inline; - font-weight: normal; + font-weight: $gl-font-weight-normal; font-size: 24px; color: $gl-text-color; } diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 6eabdc63d9e..b4a6b214e98 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -1,7 +1,7 @@ @mixin btn-default { border-radius: 3px; font-size: $gl-font-size; - font-weight: 400; + font-weight: $gl-font-weight-normal; padding: $gl-vert-padding $gl-btn-padding; &:focus, diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index 0ded4a3b423..4ce767e4cc4 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -52,13 +52,13 @@ .pika-label { color: $gl-text-color-secondary; font-size: 14px; - font-weight: normal; + font-weight: $gl-font-weight-normal; } th { padding: 2px 0; color: $note-disabled-comment-color; - font-weight: normal; + font-weight: $gl-font-weight-normal; text-transform: lowercase; border-top: 1px solid $calendar-border-color; } @@ -88,7 +88,7 @@ .is-today { .pika-day { color: inherit; - font-weight: normal; + font-weight: $gl-font-weight-normal; } } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 293aa194528..e16fbbf43b5 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -36,12 +36,12 @@ color: $common-gray; font-size: 14px; margin-bottom: 12px; - font-weight: normal; + font-weight: $gl-font-weight-normal; line-height: 24px; } .bold { - font-weight: 600; + font-weight: $gl-font-weight-bold; } .tab-content { @@ -89,7 +89,7 @@ hr { } } -.item-title { font-weight: 600; } +.item-title { font-weight: $gl-font-weight-bold; } /** FLASH message **/ .author_link, @@ -118,18 +118,18 @@ table a code { span.update-author { display: block; color: $update-author-color; - font-weight: normal; + font-weight: $gl-font-weight-normal; font-style: italic; strong { - font-weight: bold; + font-weight: $gl-font-weight-bold; font-style: normal; } } .user-mention { color: $user-mention-color; - font-weight: bold; + font-weight: $gl-font-weight-bold; } .field_with_errors { @@ -222,7 +222,7 @@ li.note { text-align: center; background: $error-bg; color: $white-light; - font-weight: bold; + font-weight: $gl-font-weight-bold; a { color: $white-light; @@ -339,7 +339,7 @@ table { .header-with-avatar { h3 { margin: 0; - font-weight: bold; + font-weight: $gl-font-weight-bold; } .username { diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 5f270e288ae..a45d5a6dca0 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -195,7 +195,7 @@ margin-top: 2px; margin-bottom: 0; font-size: 14px; - font-weight: normal; + font-weight: $gl-font-weight-normal; padding: 8px 0; background-color: $white-light; border: 1px solid $dropdown-border-color; @@ -268,7 +268,7 @@ } .dropdown-bold-header { - font-weight: 600; + font-weight: $gl-font-weight-bold; line-height: 22px; padding: 0 16px; } @@ -432,7 +432,7 @@ .dropdown-menu-user-full-name { display: block; - font-weight: 500; + font-weight: $gl-font-weight-normal; line-height: 16px; text-overflow: ellipsis; overflow: hidden; @@ -468,7 +468,7 @@ &.is-indeterminate, &.is-active { - font-weight: 600; + font-weight: $gl-font-weight-bold; color: $gl-text-color; &::before { @@ -502,7 +502,7 @@ position: relative; padding: 2px 25px 10px; margin: 0 10px 10px; - font-weight: 600; + font-weight: $gl-font-weight-bold; line-height: 1; text-align: center; text-overflow: ellipsis; @@ -685,7 +685,7 @@ .dropdown-menu-inner-title { display: block; color: $gl-text-color; - font-weight: 600; + font-weight: $gl-font-weight-bold; } .dropdown-menu-inner-content { diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 8dcaa879b3f..a5d33d410fb 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -371,7 +371,7 @@ } > .value { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } @@ -452,7 +452,7 @@ .dropdown-light-content { font-size: 14px; - font-weight: 400; + font-weight: $gl-font-weight-normal; } .dropdown-user { diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss index 38d884bc7eb..e1b086ebb2b 100644 --- a/app/assets/stylesheets/framework/flash.scss +++ b/app/assets/stylesheets/framework/flash.scss @@ -25,7 +25,7 @@ a.flash-action { margin-left: 5px; text-decoration: none; - font-weight: normal; + font-weight: $gl-font-weight-normal; border-bottom: 1px solid; &:hover { diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 61e3897f369..be96c8ee964 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -32,7 +32,7 @@ label { } &.label-light { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } @@ -73,7 +73,7 @@ label { margin-right: 0; .control-label { - font-weight: bold; + font-weight: $gl-font-weight-bold; padding-top: 4px; } @@ -157,7 +157,7 @@ label { .form-group .control-label, .form-group .control-label-full-width { - font-weight: normal; + font-weight: $gl-font-weight-normal; } .form-control::-webkit-input-placeholder { diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index b677882eba4..35bd97980e2 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -160,7 +160,7 @@ header { li { &.active a { - font-weight: bold; + font-weight: $gl-font-weight-bold; } } } @@ -250,7 +250,7 @@ header { font-size: 18px; line-height: 22px; display: inline-block; - font-weight: normal; + font-weight: $gl-font-weight-normal; color: $gl-text-color; vertical-align: top; white-space: nowrap; @@ -326,7 +326,7 @@ header { .badge { position: inherit; top: -8px; - font-weight: normal; + font-weight: $gl-font-weight-normal; margin-left: -11px; font-size: 11px; color: $white-light; diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index df2bf561194..0fb19344510 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -113,7 +113,7 @@ ul.content-list { } .title { - font-weight: 600; + font-weight: $gl-font-weight-bold; } a { @@ -212,7 +212,7 @@ ul.content-list { } .row-title { - font-weight: 600; + font-weight: $gl-font-weight-bold; } .row-second-line { diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 6f91d11b369..d40b65bb2cc 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -43,7 +43,7 @@ background: $gray-light; a { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index a28f54936be..d1f00d3ee2c 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -8,7 +8,7 @@ } .text-danger { - font-weight: bold; + font-weight: $gl-font-weight-bold; } } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 071f20fc457..e20108b171b 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -70,7 +70,7 @@ &.active a { border-bottom: 2px solid $link-underline-blue; color: $black; - font-weight: 600; + font-weight: $gl-font-weight-bold; .badge { color: $black; @@ -352,7 +352,7 @@ z-index: 300; li.active { - font-weight: bold; + font-weight: $gl-font-weight-bold; } } } diff --git a/app/assets/stylesheets/framework/page-header.scss b/app/assets/stylesheets/framework/page-header.scss index f1ecd050a0a..0c879f40930 100644 --- a/app/assets/stylesheets/framework/page-header.scss +++ b/app/assets/stylesheets/framework/page-header.scss @@ -43,7 +43,7 @@ .commit-committer-link, .commit-author-link { color: $gl-text-color; - font-weight: bold; + font-weight: $gl-font-weight-bold; } .commit-info { diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index f7a0b355bf1..d93722e2174 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -76,7 +76,7 @@ } .select2-results li.select2-result-with-children > .select2-result-label { - font-weight: 600; + font-weight: $gl-font-weight-bold; color: $gl-text-color; } @@ -227,7 +227,7 @@ } .group-name { - font-weight: bold; + font-weight: $gl-font-weight-bold; } .group-path { @@ -252,12 +252,12 @@ .namespace-result { .namespace-kind { color: $namespace-kind-color; - font-weight: normal; + font-weight: $gl-font-weight-normal; } .namespace-path { margin-left: 10px; - font-weight: bolder; + font-weight: $gl-font-weight-bold; } } @@ -283,7 +283,7 @@ padding: 0 1px; .select2-match { - font-weight: bold; + font-weight: $gl-font-weight-bold; text-decoration: none; } diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss index 5f7e1b17cc7..30c15c231d5 100644 --- a/app/assets/stylesheets/framework/snippets.scss +++ b/app/assets/stylesheets/framework/snippets.scss @@ -30,7 +30,7 @@ .snippet-title { font-size: 24px; - font-weight: 600; + font-weight: $gl-font-weight-bold; } .snippet-edited-ago { diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 6d9fa74a030..4dd31bf28cd 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -32,7 +32,7 @@ table { th { background-color: $gray-light; - font-weight: normal; + font-weight: $gl-font-weight-normal; border-bottom: none; &.wide { diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index e54cc2866a7..d5c6ddbb4a5 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -103,7 +103,7 @@ summary { padding: 4px 5px; font-size: 12px; font-style: normal; - font-weight: normal; + font-weight: $gl-font-weight-normal; display: inline-block; &.label-gray { @@ -165,7 +165,7 @@ summary { .panel-heading { padding: 6px 15px; font-size: 13px; - font-weight: normal; + font-weight: $gl-font-weight-normal; a { color: $panel-heading-link-color; diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index d13f9996518..71eec0e1a5e 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -74,7 +74,7 @@ h1 { font-size: 1.75em; - font-weight: 600; + font-weight: $gl-font-weight-bold; margin: 24px 0 16px; padding-bottom: 0.3em; border-bottom: 1px solid $white-dark; @@ -87,7 +87,7 @@ h2 { font-size: 1.5em; - font-weight: 600; + font-weight: $gl-font-weight-bold; margin: 24px 0 16px; padding-bottom: 0.3em; border-bottom: 1px solid $white-dark; @@ -280,7 +280,7 @@ body { margin-top: $gl-padding; line-height: 1.3; font-size: 1.25em; - font-weight: 600; + font-weight: $gl-font-weight-bold; &:last-child { margin-bottom: 0; @@ -291,7 +291,7 @@ body { margin-top: 0; line-height: 1.3; font-size: 1.25em; - font-weight: 600; + font-weight: $gl-font-weight-bold; margin: 12px 7px; } @@ -302,11 +302,11 @@ h4, h5, h6 { color: $gl-text-color; - font-weight: 600; + font-weight: $gl-font-weight-bold; } .light-header { - font-weight: 600; + font-weight: $gl-font-weight-bold; } /** CODE **/ diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 225d116e9c7..8a2e64f7bf5 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -111,6 +111,8 @@ $well-light-text-color: #5b6169; * Text */ $gl-font-size: 14px; +$gl-font-weight-normal: 400; +$gl-font-weight-bold: 600; $gl-text-color: #2e2e2e; $gl-text-color-secondary: #707070; $gl-text-color-tertiary: #949494; diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index b1ff2659131..5f9756bf58a 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -69,7 +69,7 @@ .well-centered { h1 { - font-weight: normal; + font-weight: $gl-font-weight-normal; text-align: center; font-size: 48px; } diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index 6e3829d994f..f0ac9b46f91 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -204,11 +204,11 @@ $dark-il: #de935f; .cs { color: $dark-cs; } /* Comment.Special */ .gd { color: $dark-gd; } /* Generic.Deleted */ .ge { font-style: italic; } /* Generic.Emph */ - .gh { color: $dark-gh; font-weight: bold; } /* Generic.Heading */ + .gh { color: $dark-gh; font-weight: $gl-font-weight-bold; } /* Generic.Heading */ .gi { color: $dark-gi; } /* Generic.Inserted */ - .gp { color: $dark-gp; font-weight: bold; } /* Generic.Prompt */ - .gs { font-weight: bold; } /* Generic.Strong */ - .gu { color: $dark-gu; font-weight: bold; } /* Generic.Subheading */ + .gp { color: $dark-gp; font-weight: $gl-font-weight-bold; } /* Generic.Prompt */ + .gs { font-weight: $gl-font-weight-bold; } /* Generic.Strong */ + .gu { color: $dark-gu; font-weight: $gl-font-weight-bold; } /* Generic.Subheading */ .kc { color: $dark-kc; } /* Keyword.Constant */ .kd { color: $dark-kd; } /* Keyword.Declaration */ .kn { color: $dark-kn; } /* Keyword.Namespace */ diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index 68eb0c7720f..eba7919ada9 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -203,7 +203,7 @@ $monokai-gi: #a6e22e; .c1 { color: $monokai-c1; } /* Comment.Single */ .cs { color: $monokai-cs; } /* Comment.Special */ .ge { font-style: italic; } /* Generic.Emph */ - .gs { font-weight: bold; } /* Generic.Strong */ + .gs { font-weight: $gl-font-weight-bold; } /* Generic.Strong */ .kc { color: $monokai-kc; } /* Keyword.Constant */ .kd { color: $monokai-kd; } /* Keyword.Declaration */ .kn { color: $monokai-kn; } /* Keyword.Namespace */ diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index 2cc968c32f2..ba53ef0352b 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -231,7 +231,7 @@ $solarized-dark-il: #2aa198; .gi { color: $solarized-dark-gi; } /* Generic.Inserted */ .go { color: $solarized-dark-go; } /* Generic.Output */ .gp { color: $solarized-dark-gp; } /* Generic.Prompt */ - .gs { color: $solarized-dark-gs; font-weight: bold; } /* Generic.Strong */ + .gs { color: $solarized-dark-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */ .gu { color: $solarized-dark-gu; } /* Generic.Subheading */ .gt { color: $solarized-dark-gt; } /* Generic.Traceback */ .kc { color: $solarized-dark-kc; } /* Keyword.Constant */ diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss index b61b85a2cd1..e9fccf1b58a 100644 --- a/app/assets/stylesheets/highlight/solarized_light.scss +++ b/app/assets/stylesheets/highlight/solarized_light.scss @@ -239,7 +239,7 @@ $solarized-light-il: #2aa198; .gi { color: $solarized-light-gi; } /* Generic.Inserted */ .go { color: $solarized-light-go; } /* Generic.Output */ .gp { color: $solarized-light-gp; } /* Generic.Prompt */ - .gs { color: $solarized-light-gs; font-weight: bold; } /* Generic.Strong */ + .gs { color: $solarized-light-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */ .gu { color: $solarized-light-gu; } /* Generic.Subheading */ .gt { color: $solarized-light-gt; } /* Generic.Traceback */ .kc { color: $solarized-light-kc; } /* Keyword.Constant */ diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 578f1902cce..65b140cd7f8 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -211,12 +211,12 @@ $white-gc-bg: #eaf2f5; .hll { background-color: $white-hll-bg; } .c { color: $white-c; font-style: italic; } .err { color: $white-err; background-color: $white-err-bg; } - .k { font-weight: bold; } - .o { font-weight: bold; } + .k { font-weight: $gl-font-weight-bold; } + .o { font-weight: $gl-font-weight-bold; } .cm { color: $white-cm; font-style: italic; } - .cp { color: $white-cp; font-weight: bold; } + .cp { color: $white-cp; font-weight: $gl-font-weight-bold; } .c1 { color: $white-c1; font-style: italic; } - .cs { color: $white-cs; font-weight: bold; font-style: italic; } + .cs { color: $white-cs; font-weight: $gl-font-weight-bold; font-style: italic; } .gd { color: $white-gd; background-color: $white-gd-bg; } .gd .x { color: $white-gd-x; background-color: $white-gd-x-bg; } .ge { font-style: italic; } @@ -226,29 +226,29 @@ $white-gc-bg: #eaf2f5; .gi .x { color: $white-gi-x; background-color: $white-gi-x-bg; } .go { color: $white-go; } .gp { color: $white-gp; } - .gs { font-weight: bold; } - .gu { color: $white-gu; font-weight: bold; } + .gs { font-weight: $gl-font-weight-bold; } + .gu { color: $white-gu; font-weight: $gl-font-weight-bold; } .gt { color: $white-gt; } - .kc { font-weight: bold; } - .kd { font-weight: bold; } - .kn { font-weight: bold; } - .kp { font-weight: bold; } - .kr { font-weight: bold; } - .kt { color: $white-kt; font-weight: bold; } + .kc { font-weight: $gl-font-weight-bold; } + .kd { font-weight: $gl-font-weight-bold; } + .kn { font-weight: $gl-font-weight-bold; } + .kp { font-weight: $gl-font-weight-bold; } + .kr { font-weight: $gl-font-weight-bold; } + .kt { color: $white-kt; font-weight: $gl-font-weight-bold; } .m { color: $white-m; } .s { color: $white-s; } .n { color: $white-n; } .na { color: $white-na; } .nb { color: $white-nb; } - .nc { color: $white-nc; font-weight: bold; } + .nc { color: $white-nc; font-weight: $gl-font-weight-bold; } .no { color: $white-no; } .ni { color: $white-ni; } - .ne { color: $white-ne; font-weight: bold; } - .nf { color: $white-nf; font-weight: bold; } + .ne { color: $white-ne; font-weight: $gl-font-weight-bold; } + .nf { color: $white-nf; font-weight: $gl-font-weight-bold; } .nn { color: $white-nn; } .nt { color: $white-nt; } .nv { color: $white-nv; } - .ow { font-weight: bold; } + .ow { font-weight: $gl-font-weight-bold; } .w { color: $white-w; } .mf { color: $white-mf; } .mh { color: $white-mh; } diff --git a/app/assets/stylesheets/mailers/highlighted_diff_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss index ea40f449134..fbe538ad1d7 100644 --- a/app/assets/stylesheets/mailers/highlighted_diff_email.scss +++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss @@ -152,12 +152,12 @@ span.highlight_word { .hll { background-color: $highlighted-hll-bg; } .c { color: $highlighted-c; font-style: italic; } .err { color: $highlighted-err; background-color: $highlighted-err-bg; } -.k { font-weight: bold; } -.o { font-weight: bold; } +.k { font-weight: $gl-font-weight-bold; } +.o { font-weight: $gl-font-weight-bold; } .cm { color: $highlighted-cm; font-style: italic; } -.cp { color: $highlighted-cp; font-weight: bold; } +.cp { color: $highlighted-cp; font-weight: $gl-font-weight-bold; } .c1 { color: $highlighted-c1; font-style: italic; } -.cs { color: $highlighted-cs; font-weight: bold; font-style: italic; } +.cs { color: $highlighted-cs; font-weight: $gl-font-weight-bold; font-style: italic; } .gd { color: $highlighted-gd; background-color: $highlighted-gd-bg; } .gd .x { color: $highlighted-gd; background-color: $highlighted-gd-x-bg; } .ge { font-style: italic; } @@ -167,29 +167,29 @@ span.highlight_word { .gi .x { color: $highlighted-gi; background-color: $highlighted-gi-x-bg; } .go { color: $highlighted-go; } .gp { color: $highlighted-gp; } -.gs { font-weight: bold; } -.gu { color: $highlighted-gu; font-weight: bold; } +.gs { font-weight: $gl-font-weight-bold; } +.gu { color: $highlighted-gu; font-weight: $gl-font-weight-bold; } .gt { color: $highlighted-gt; } -.kc { font-weight: bold; } -.kd { font-weight: bold; } -.kn { font-weight: bold; } -.kp { font-weight: bold; } -.kr { font-weight: bold; } -.kt { color: $highlighted-kt; font-weight: bold; } +.kc { font-weight: $gl-font-weight-bold; } +.kd { font-weight: $gl-font-weight-bold; } +.kn { font-weight: $gl-font-weight-bold; } +.kp { font-weight: $gl-font-weight-bold; } +.kr { font-weight: $gl-font-weight-bold; } +.kt { color: $highlighted-kt; font-weight: $gl-font-weight-bold; } .m { color: $highlighted-m; } .s { color: $highlighted-s; } .n { color: $highlighted-n; } .na { color: $highlighted-na; } .nb { color: $highlighted-nb; } -.nc { color: $highlighted-nc; font-weight: bold; } +.nc { color: $highlighted-nc; font-weight: $gl-font-weight-bold; } .no { color: $highlighted-no; } .ni { color: $highlighted-ni; } -.ne { color: $highlighted-ne; font-weight: bold; } -.nf { color: $highlighted-nf; font-weight: bold; } +.ne { color: $highlighted-ne; font-weight: $gl-font-weight-bold; } +.nf { color: $highlighted-nf; font-weight: $gl-font-weight-bold; } .nn { color: $highlighted-nn; } .nt { color: $highlighted-nt; } .nv { color: $highlighted-nv; } -.ow { font-weight: bold; } +.ow { font-weight: $gl-font-weight-bold; } .w { color: $highlighted-w; } .mf { color: $highlighted-mf; } .mh { color: $highlighted-mh; } diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss index 3e2f23e6b2a..54fa4109f8b 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/new_nav.scss @@ -134,7 +134,7 @@ header.navbar-gitlab-new { li { .badge { box-shadow: none; - font-weight: 600; + font-weight: $gl-font-weight-bold; } } } @@ -193,7 +193,7 @@ header.navbar-gitlab-new { &.active > a { box-shadow: inset 0 -3px 0 $indigo-500; color: $white-light; - font-weight: 700; + font-weight: $gl-font-weight-bold; } > a { @@ -371,7 +371,7 @@ header.navbar-gitlab-new { > a { &:last-of-type:not(:first-child) { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } } @@ -411,7 +411,7 @@ header.navbar-gitlab-new { .breadcrumbs-sub-title { margin: 2px 0; font-size: 16px; - font-weight: normal; + font-weight: $gl-font-weight-normal; line-height: 1; ul { @@ -430,7 +430,7 @@ header.navbar-gitlab-new { } &:last-child a { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index cee5b22adb9..a74d53b4f68 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -46,7 +46,7 @@ $new-sidebar-collapsed-width: 50px; a { border-bottom: 1px solid $border-color; - font-weight: 600; + font-weight: $gl-font-weight-bold; display: flex; align-items: center; padding: 10px 16px 10px 10px; @@ -160,7 +160,7 @@ $new-sidebar-collapsed-width: 50px; > a { color: $active-color; - font-weight: 700; + font-weight: $gl-font-weight-bold; } svg { @@ -308,7 +308,7 @@ $new-sidebar-collapsed-width: 50px; .badge { color: $active-color; - font-weight: 600; + font-weight: $gl-font-weight-bold; } .sidebar-sub-level-items { @@ -474,6 +474,6 @@ $new-sidebar-collapsed-width: 50px; border-bottom-color: $active-border; .badge { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index e5b467a2691..0f3074076ce 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -471,7 +471,7 @@ padding-right: 35px; > strong { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } } diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 486424fb729..3d04df8d820 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -277,7 +277,7 @@ } .trigger-build-variable { - font-weight: normal; + font-weight: $gl-font-weight-normal; color: $code-color; } @@ -378,7 +378,7 @@ } &.active { - font-weight: bold; + font-weight: $gl-font-weight-bold; .fa-arrow-right { display: block; diff --git a/app/assets/stylesheets/pages/ci_projects.scss b/app/assets/stylesheets/pages/ci_projects.scss index 7b4eb689f1b..bf6a48889bf 100644 --- a/app/assets/stylesheets/pages/ci_projects.scss +++ b/app/assets/stylesheets/pages/ci_projects.scss @@ -22,7 +22,7 @@ vertical-align: middle !important; a { - font-weight: normal; + font-weight: $gl-font-weight-normal; text-decoration: none; } } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 46fbfe5f91e..c051d37aad6 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -213,7 +213,7 @@ .commit-sha { font-size: 14px; - font-weight: 600; + font-weight: $gl-font-weight-bold; } } @@ -286,6 +286,9 @@ .gpg-status-box { + padding: 2px 10px; + margin-right: $gl-padding; + &:empty { display: none; } @@ -303,7 +306,7 @@ .gpg-popover-status { display: flex; align-items: center; - font-weight: normal; + font-weight: $gl-font-weight-normal; line-height: 1.5; } @@ -314,7 +317,6 @@ &.valid { svg { border: 1px solid $brand-success; - fill: $brand-success; } } @@ -322,7 +324,6 @@ &.invalid { svg { border: 1px solid $common-gray-light; - fill: $common-gray-light; } } diff --git a/app/assets/stylesheets/pages/convdev_index.scss b/app/assets/stylesheets/pages/convdev_index.scss index 0413114c279..16702442f50 100644 --- a/app/assets/stylesheets/pages/convdev_index.scss +++ b/app/assets/stylesheets/pages/convdev_index.scss @@ -23,7 +23,7 @@ $space-between-cards: 8px; line-height: 1; color: $gl-text-color-secondary; margin-left: 8px; - font-weight: 500; + font-weight: $gl-font-weight-normal; a { font-size: 18px; @@ -139,7 +139,7 @@ $space-between-cards: 8px; .card-score-value { font-size: 16px; color: $gl-text-color; - font-weight: 500; + font-weight: $gl-font-weight-normal; } .card-score-big { @@ -147,7 +147,7 @@ $space-between-cards: 8px; border-bottom: 1px solid $border-color; font-size: 22px; padding: 10px 0; - font-weight: 500; + font-weight: $gl-font-weight-normal; } .card-buttons { diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index 6753eb08285..2a92673d9fa 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -68,7 +68,7 @@ } .stage-name { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } @@ -93,7 +93,7 @@ .header { font-size: 30px; line-height: 38px; - font-weight: normal; + font-weight: $gl-font-weight-normal; margin: 0; } @@ -130,7 +130,7 @@ &.title { line-height: 19px; font-size: 14px; - font-weight: 600; + font-weight: $gl-font-weight-bold; color: $gl-text-color; } @@ -211,7 +211,7 @@ box-shadow: inset 2px 0 0 0 $active-item-blue; .stage-name { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } @@ -404,7 +404,7 @@ color: $gl-link-color; line-height: 1.3; vertical-align: top; - font-weight: normal; + font-weight: $gl-font-weight-normal; } .fa { diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 913a1a95dca..8cbf0ec6180 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -40,7 +40,7 @@ // "Changes suppressed. Click to show." link .show-suppressed-diff { font-size: 110%; - font-weight: bold; + font-weight: $gl-font-weight-bold; } } @@ -104,7 +104,7 @@ a { float: left; width: 35px; - font-weight: normal; + font-weight: $gl-font-weight-normal; &[disabled] { cursor: default; @@ -395,7 +395,7 @@ background-color: transparent; border: 0; color: $gl-link-color; - font-weight: 600; + font-weight: $gl-font-weight-bold; &:hover, &:focus { diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 00ebf4e26ac..a8d2ae0af28 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -6,7 +6,7 @@ } .environments-folder-name { - font-weight: normal; + font-weight: $gl-font-weight-normal; padding-top: 20px; } @@ -246,13 +246,13 @@ } .text-metric-bold { - font-weight: 600; + font-weight: $gl-font-weight-bold; } .label-axis-text, .text-metric-usage { fill: $black; - font-weight: 500; + font-weight: $gl-font-weight-normal; font-size: 12px; } diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 4c3fa1fb8d4..1723d716805 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -57,7 +57,7 @@ .event-title { @include str-truncated(calc(100% - 174px)); - font-weight: 600; + font-weight: $gl-font-weight-bold; color: $list-text-color; } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 49839a9b528..ab5a901da71 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -271,7 +271,7 @@ } .light { - font-weight: normal; + font-weight: $gl-font-weight-normal; } .no-value { @@ -306,7 +306,7 @@ display: block; margin-top: 4px; font-size: 13px; - font-weight: normal; + font-weight: $gl-font-weight-normal; } .hide-expanded { @@ -689,7 +689,7 @@ .issuable-info, .task-status, .issuable-updated-at { - font-weight: normal; + font-weight: $gl-font-weight-normal; color: $gl-text-color-secondary; a { diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 8cdb3f34ae5..e2177f96aee 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -75,7 +75,7 @@ ul.related-merge-requests > li { .merge-requests-title, .related-branches-title { font-size: 16px; - font-weight: 600; + font-weight: $gl-font-weight-bold; } .merge-request-id { @@ -244,7 +244,7 @@ ul.related-merge-requests > li { strong { display: block; - font-weight: 600; + font-weight: $gl-font-weight-bold; } } } diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 3cbe8dededb..d4dc43035eb 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -22,7 +22,7 @@ } h1:first-child { - font-weight: normal; + font-weight: $gl-font-weight-normal; margin-bottom: 0.68em; margin-top: 0; font-size: 34px; @@ -38,7 +38,7 @@ } a { - font-weight: bold; + font-weight: $gl-font-weight-bold; } } @@ -54,7 +54,7 @@ padding: 15px; .login-heading h3 { - font-weight: 300; + font-weight: $gl-font-weight-normal; line-height: 1.5; margin: 0 0 10px; } @@ -186,7 +186,7 @@ } label { - font-weight: normal; + font-weight: $gl-font-weight-normal; } .submit-container { diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index e7c07ef67f0..a385eb359e1 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -46,7 +46,7 @@ } strong { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } @@ -221,7 +221,7 @@ } .member { - font-weight: bold; + font-weight: $gl-font-weight-bold; overflow-wrap: break-word; word-break: break-all; } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 6bb013cca85..d1678a17aaf 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -197,7 +197,7 @@ @extend .ref-name; color: $gl-text-color; - font-weight: 600; + font-weight: $gl-font-weight-bold; overflow: hidden; word-break: break-all; @@ -228,7 +228,7 @@ .mr-widget-body { h4 { float: left; - font-weight: 600; + font-weight: $gl-font-weight-bold; font-size: 14px; line-height: inherit; margin-top: 0; @@ -239,7 +239,7 @@ } time { - font-weight: normal; + font-weight: $gl-font-weight-normal; } } @@ -249,7 +249,7 @@ } label { - font-weight: normal; + font-weight: $gl-font-weight-normal; } .spacing { @@ -257,12 +257,12 @@ } .bold { - font-weight: 600; + font-weight: $gl-font-weight-bold; color: $gl-gray-light; } .state-label { - font-weight: 600; + font-weight: $gl-font-weight-bold; padding-right: 10px; } @@ -336,7 +336,7 @@ .text { span { - font-weight: 600; + font-weight: $gl-font-weight-bold; } p { @@ -505,7 +505,7 @@ .panel-new-merge-request { .panel-heading { padding: 5px 10px; - font-weight: 600; + font-weight: $gl-font-weight-bold; line-height: 25px; } diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 55e0ee1936e..32039936be7 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -7,7 +7,7 @@ padding: 10px 16px; h4 { - font-weight: bold; + font-weight: $gl-font-weight-bold; } .progress { @@ -81,7 +81,7 @@ } .remaining-days strong { - font-weight: normal; + font-weight: $gl-font-weight-normal; } .milestone-stat { diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index b4468d6d0a2..9558924bbcb 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -188,7 +188,7 @@ .close { color: $white-light; opacity: 0.85; - font-weight: normal; + font-weight: $gl-font-weight-normal; &:hover { opacity: 1; diff --git a/app/assets/stylesheets/pages/pipeline_schedules.scss b/app/assets/stylesheets/pages/pipeline_schedules.scss index dc1654e006e..7e2297c283f 100644 --- a/app/assets/stylesheets/pages/pipeline_schedules.scss +++ b/app/assets/stylesheets/pages/pipeline_schedules.scss @@ -12,7 +12,7 @@ .interval-pattern-form-group { label { margin-right: 10px; - font-weight: normal; + font-weight: $gl-font-weight-normal; &[for='custom'] { margin-right: 0; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 85d1905ad40..a408bde37d6 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -128,7 +128,7 @@ .branch-commit { .ref-name { - font-weight: bold; + font-weight: $gl-font-weight-bold; max-width: 120px; overflow: hidden; display: inline-block; @@ -272,7 +272,7 @@ .build-name { float: right; - font-weight: 500; + font-weight: $gl-font-weight-normal; } .ci-status-icon-failed svg { @@ -281,7 +281,7 @@ .stage { color: $gl-text-color-secondary; - font-weight: 500; + font-weight: $gl-font-weight-normal; vertical-align: middle; } } @@ -420,7 +420,7 @@ .stage-name { margin: 0 0 15px 10px; - font-weight: bold; + font-weight: $gl-font-weight-bold; width: 176px; white-space: nowrap; overflow: hidden; @@ -580,7 +580,7 @@ vertical-align: bottom; display: inline-block; position: relative; - font-weight: normal; + font-weight: $gl-font-weight-normal; } @mixin mini-pipeline-graph-color($color-light, $color-main, $color-dark) { @@ -724,7 +724,7 @@ button.mini-pipeline-graph-dropdown-toggle { .mini-pipeline-graph-dropdown-item { padding: 3px 7px 4px; clear: both; - font-weight: normal; + font-weight: $gl-font-weight-normal; line-height: 1.428571429; white-space: nowrap; margin: 0 5px; diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 14ad06b0ac2..c5d6ff66dd6 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -83,7 +83,7 @@ &::after { content: "\00B7"; // Middle Dot padding: 0 6px; - font-weight: bold; + font-weight: $gl-font-weight-bold; } &:last-child { @@ -277,7 +277,7 @@ table.u2f-registrations { .oauth-application-show { .scope-name { - font-weight: 600; + font-weight: $gl-font-weight-bold; } .scopes-list { diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index d01326637ea..39c4264e496 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -2,7 +2,7 @@ margin: -16px; .alert-link { - font-weight: normal; + font-weight: $gl-font-weight-normal; } } @@ -114,7 +114,7 @@ margin-top: 10px; margin-bottom: 10px; font-size: 24px; - font-weight: 400; + font-weight: $gl-font-weight-normal; line-height: 1; word-wrap: break-word; @@ -259,7 +259,7 @@ border-width: 1px; border-style: solid; font-size: 13px; - font-weight: 600; + font-weight: $gl-font-weight-bold; line-height: 13px; letter-spacing: .4px; padding: 6px 14px; @@ -309,7 +309,7 @@ } .option-title { - font-weight: normal; + font-weight: $gl-font-weight-normal; display: inline-block; color: $gl-text-color; } @@ -575,7 +575,7 @@ a.deploy-project-label { color: $gl-text-color-tertiary; transform: translateY(-50%); font-size: 12px; - font-weight: bold; + font-weight: $gl-font-weight-bold; line-height: 20px; // Mobile @@ -826,7 +826,7 @@ pre.light-well { .new-protected-tag { label { margin-top: 6px; - font-weight: normal; + font-weight: $gl-font-weight-normal; } } @@ -853,7 +853,7 @@ pre.light-well { } &.is-active { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } @@ -952,7 +952,7 @@ pre.light-well { &::before { font-family: FontAwesome; - font-weight: normal; + font-weight: $gl-font-weight-normal; font-style: normal; } } diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 1f4d4698199..37971d6bd3a 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -267,7 +267,7 @@ display: inline-block; font-size: 10px; text-transform: uppercase; - font-weight: bold; + font-weight: $gl-font-weight-bold; color: $gray-darkest; white-space: nowrap; overflow: hidden; diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss index 57c73295d1e..6cac37a4e28 100644 --- a/app/assets/stylesheets/pages/runners.scss +++ b/app/assets/stylesheets/pages/runners.scss @@ -30,7 +30,7 @@ } h4 { - font-weight: normal; + font-weight: $gl-font-weight-normal; } } diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index b9818ffcf42..8d73246223d 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -94,7 +94,7 @@ input[type="checkbox"]:hover { &::before { font-family: FontAwesome; - font-weight: normal; + font-weight: $gl-font-weight-normal; font-style: normal; } } diff --git a/app/assets/stylesheets/pages/sherlock.scss b/app/assets/stylesheets/pages/sherlock.scss index 23a9c2ada80..bfe065dbbaf 100644 --- a/app/assets/stylesheets/pages/sherlock.scss +++ b/app/assets/stylesheets/pages/sherlock.scss @@ -29,5 +29,5 @@ table .sherlock-code { .sherlock-line-samples-table .slow { color: $red-500; - font-weight: bold; + font-weight: $gl-font-weight-bold; } diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index d7a9dda3770..5b9fafe31bd 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -108,14 +108,14 @@ margin: 0; float: none; display: inline-block; - font-weight: normal; + font-weight: $gl-font-weight-normal; padding: 0 5px; line-height: inherit; font-size: 14px; } .action-name { - font-weight: normal; + font-weight: $gl-font-weight-normal; } .todo-body { @@ -262,6 +262,6 @@ } a { - font-weight: 600; + font-weight: $gl-font-weight-bold; } } diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 0028e207f3e..224eee90a3f 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -231,7 +231,7 @@ } .upload-link { - font-weight: normal; + font-weight: $gl-font-weight-normal; color: $md-link-color; } diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss index 798e060a261..48ac5b21db8 100644 --- a/app/assets/stylesheets/pages/ui_dev_kit.scss +++ b/app/assets/stylesheets/pages/ui_dev_kit.scss @@ -1,7 +1,7 @@ .gitlab-ui-dev-kit { > h2 { margin: 35px 0 20px; - font-weight: bold; + font-weight: $gl-font-weight-bold; } .example { diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index fa6bdd297eb..b7d4e7bf582 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -37,7 +37,7 @@ } .light { - font-weight: normal; + font-weight: $gl-font-weight-normal; color: $gl-text-color-secondary; } @@ -89,7 +89,7 @@ h3 { font-size: 19px; - font-weight: normal; + font-weight: $gl-font-weight-normal; margin: $gl-padding 0; } } diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss index b085c56390d..c7297a34ad8 100644 --- a/app/assets/stylesheets/pages/xterm.scss +++ b/app/assets/stylesheets/pages/xterm.scss @@ -281,7 +281,7 @@ $xterm-fg-255: #eee; .term-bold { - font-weight: bold; + font-weight: $gl-font-weight-bold; } .term-italic { diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss index 113e6e86bb5..b07a5ae22cd 100644 --- a/app/assets/stylesheets/print.scss +++ b/app/assets/stylesheets/print.scss @@ -17,7 +17,7 @@ .wiki h3 { font-size: 18px; - font-weight: bold; + font-weight: 600; } header, diff --git a/app/controllers/admin/logs_controller.rb b/app/controllers/admin/logs_controller.rb index b999018dde4..bdc4332ae69 100644 --- a/app/controllers/admin/logs_controller.rb +++ b/app/controllers/admin/logs_controller.rb @@ -1,2 +1,11 @@ class Admin::LogsController < Admin::ApplicationController + def show + @loggers = [ + Gitlab::AppLogger, + Gitlab::GitLogger, + Gitlab::EnvironmentLogger, + Gitlab::SidekiqLogger, + Gitlab::RepositoryCheckLogger + ] + end end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 0b6cd71e651..50cf2643390 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -3,9 +3,9 @@ class Admin::ProjectsController < Admin::ApplicationController before_action :group, only: [:show, :transfer] def index - finder = Admin::ProjectsFinder.new(params: params, current_user: current_user) - @projects = finder.execute - @sort = finder.sort + params[:sort] ||= 'latest_activity_desc' + @sort = params[:sort] + @projects = Admin::ProjectsFinder.new(params: params, current_user: current_user).execute respond_to do |format| format.html diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index d54a1111f11..daa5c88aae0 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -4,7 +4,6 @@ class Projects::ServicesController < Projects::ApplicationController # Authorize before_action :authorize_admin_project! before_action :service, only: [:edit, :update, :test] - before_action :update_service, only: [:update, :test] respond_to :html @@ -14,6 +13,8 @@ class Projects::ServicesController < Projects::ApplicationController end def update + @service.attributes = service_params[:service] + if @service.save(context: :manual_change) redirect_to(project_settings_integrations_path(@project), notice: success_message) else @@ -24,7 +25,7 @@ class Projects::ServicesController < Projects::ApplicationController def test message = {} - if @service.can_test? + if @service.can_test? && @service.update_attributes(service_params[:service]) data = @service.test_data(project, current_user) outcome = @service.test(data) @@ -50,10 +51,6 @@ class Projects::ServicesController < Projects::ApplicationController end end - def update_service - @service.assign_attributes(service_params[:service]) - end - def service @service ||= @project.find_or_initialize_service(params[:id]) end diff --git a/app/finders/admin/projects_finder.rb b/app/finders/admin/projects_finder.rb index 7176bfe22d6..d6bcd939522 100644 --- a/app/finders/admin/projects_finder.rb +++ b/app/finders/admin/projects_finder.rb @@ -1,33 +1,67 @@ class Admin::ProjectsFinder - attr_reader :sort, :namespace_id, :visibility_level, :with_push, - :abandoned, :last_repository_check_failed, :archived, - :personal, :name, :page, :current_user + attr_reader :params, :current_user def initialize(params:, current_user:) + @params = params @current_user = current_user - @sort = params.fetch(:sort) { 'latest_activity_desc' } - @namespace_id = params[:namespace_id] - @visibility_level = params[:visibility_level] - @with_push = params[:with_push] - @abandoned = params[:abandoned] - @last_repository_check_failed = params[:last_repository_check_failed] - @archived = params[:archived] - @personal = params[:personal] - @name = params[:name] - @page = params[:page] end def execute items = Project.without_deleted.with_statistics - items = items.in_namespace(namespace_id) if namespace_id.present? - items = items.where(visibility_level: visibility_level) if visibility_level.present? - items = items.with_push if with_push.present? - items = items.abandoned if abandoned.present? - items = items.where(last_repository_check_failed: true) if last_repository_check_failed.present? - items = items.non_archived unless archived.present? - items = items.personal(current_user) if personal.present? - items = items.search(name) if name.present? - items = items.sort(sort) - items.includes(:namespace).order("namespaces.path, projects.name ASC").page(page) + items = by_namespace_id(items) + items = by_visibilty_level(items) + items = by_with_push(items) + items = by_abandoned(items) + items = by_last_repository_check_failed(items) + items = by_archived(items) + items = by_personal(items) + items = by_name(items) + items = sort(items) + items.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]) + end + + private + + def by_namespace_id(items) + params[:namespace_id].present? ? items.in_namespace(params[:namespace_id]) : items + end + + def by_visibilty_level(items) + params[:visibility_level].present? ? items.where(visibility_level: params[:visibility_level]) : items + end + + def by_with_push(items) + params[:with_push].present? ? items.with_push : items + end + + def by_abandoned(items) + params[:abandoned].present? ? items.abandoned : items + end + + def by_last_repository_check_failed(items) + params[:last_repository_check_failed].present? ? items.where(last_repository_check_failed: true) : items + end + + def by_archived(items) + if params[:archived] == 'only' + items.archived + elsif params[:archived].blank? + items.non_archived + else + items + end + end + + def by_personal(items) + params[:personal].present? ? items.personal(current_user) : items + end + + def by_name(items) + params[:name].present? ? items.search(params[:name]) : items + end + + def sort(items) + sort = params.fetch(:sort) { 'latest_activity_desc' } + items.sort(sort) end end diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index aa80dfc3f37..fa6fea2588a 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -125,9 +125,18 @@ class ProjectsFinder < UnionFinder end def by_archived(projects) - # Back-compatibility with the places where `params[:archived]` can be set explicitly to `false` - params[:non_archived] = !Gitlab::Utils.to_boolean(params[:archived]) if params.key?(:archived) - - params[:non_archived] ? projects.non_archived : projects + if params[:non_archived] + projects.non_archived + elsif params.key?(:archived) # Back-compatibility with the places where `params[:archived]` can be set explicitly to `false` + if params[:archived] == 'only' + projects.archived + elsif Gitlab::Utils.to_boolean(params[:archived]) + projects + else + projects.non_archived + end + else + projects + end end end diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index 4b51269533c..a4c226a6aad 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -12,11 +12,18 @@ module AvatarsHelper avatar_size = options[:size] || 16 user_name = options[:user].try(:name) || options[:user_name] avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size) - data_attributes = { container: 'body' } + has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip] + data_attributes = {} + css_class = %W[avatar s#{avatar_size}].push(*options[:css_class]) + + if has_tooltip + css_class.push('has-tooltip') + data_attributes = { container: 'body' } + end image_tag( avatar_url, - class: %W[avatar has-tooltip s#{avatar_size}].push(*options[:css_class]), + class: css_class, alt: "#{user_name}'s avatar", title: user_name, data: data_attributes, diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 72e26b64e60..9651f9733f9 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -114,7 +114,7 @@ module CommitsHelper end def commit_signature_badge_classes(additional_classes) - %w(btn status-box gpg-status-box) + Array(additional_classes) + %w(btn gpg-status-box) + Array(additional_classes) end protected diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index c6f98e7e782..b331693c789 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -181,6 +181,7 @@ module EventsHelper end def event_commit_title(message) + message ||= '' (message.split("\n").first || "").truncate(70) rescue "--broken encoding" diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 4692fb5644a..095192e9894 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -46,7 +46,10 @@ module Ci before_save :ensure_token before_destroy { unscoped_project } - after_create :execute_hooks + after_create do |build| + run_after_commit { BuildHooksWorker.perform_async(build.id) } + end + after_commit :update_project_statistics_after_save, on: [:create, :update] after_commit :update_project_statistics, on: :destroy diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index ea7331cb27f..2d40f8012a3 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -393,7 +393,8 @@ module Ci def predefined_variables [ { key: 'CI_PIPELINE_ID', value: id.to_s, public: true }, - { key: 'CI_CONFIG_PATH', value: ci_yaml_file_path, public: true } + { key: 'CI_CONFIG_PATH', value: ci_yaml_file_path, public: true }, + { key: 'CI_PIPELINE_SOURCE', value: source.to_s, public: true } ] end diff --git a/app/models/event.rb b/app/models/event.rb index 15ee170ca75..996768a267b 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -406,7 +406,7 @@ class Event < ActiveRecord::Base def body? if push? - push_with_commits? || rm_ref? + push_with_commits? elsif note? true else diff --git a/app/models/issue.rb b/app/models/issue.rb index 043da9967a1..b9aa937d2f9 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -50,7 +50,10 @@ class Issue < ActiveRecord::Base scope :preload_associations, -> { preload(:labels, project: :namespace) } + scope :public_only, -> { where(confidential: false) } + after_save :expire_etag_cache + after_commit :update_project_counter_caches, on: :destroy attr_spammable :title, spam_title: true attr_spammable :description, spam_description: true @@ -266,6 +269,10 @@ class Issue < ActiveRecord::Base end end + def update_project_counter_caches + Projects::OpenIssuesCountService.new(project).refresh_cache + end + private # Returns `true` if the given User can read the current Issue. diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index f028d2395c1..dbc73ed3cd4 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -31,6 +31,7 @@ class MergeRequest < ActiveRecord::Base after_create :ensure_merge_request_diff, unless: :importing? after_update :reload_diff_if_branch_changed + after_commit :update_project_counter_caches, on: :destroy # When this attribute is true some MR validation is ignored # It allows us to close or modify broken merge requests @@ -682,9 +683,8 @@ class MergeRequest < ActiveRecord::Base if !include_description && closes_issues_references.present? message << "Closes #{closes_issues_references.to_sentence}" end - message << "#{description}" if include_description && description.present? - message << "See merge request #{to_reference}" + message << "See merge request #{to_reference(full: true)}" message.join("\n\n") end @@ -936,6 +936,10 @@ class MergeRequest < ActiveRecord::Base true end + def update_project_counter_caches + Projects::OpenMergeRequestsCountService.new(target_project).refresh_cache + end + private def write_ref diff --git a/app/models/project.rb b/app/models/project.rb index 37f4dd08355..d5324ceac31 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -247,6 +247,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 :archived, -> { where(archived: true) } scope :non_archived, -> { where(archived: false) } scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) } @@ -1017,7 +1018,7 @@ class Project < ActiveRecord::Base name: name, description: description, web_url: web_url, - avatar_url: avatar_url, + avatar_url: avatar_url(only_path: false), git_ssh_url: ssh_url_to_repo, git_http_url: http_url_to_repo, namespace: namespace.name, @@ -1167,7 +1168,11 @@ class Project < ActiveRecord::Base end def open_issues_count - issues.opened.count + Projects::OpenIssuesCountService.new(self).count + end + + def open_merge_requests_count + Projects::OpenMergeRequestsCountService.new(self).count end def visibility_level_allowed_as_fork?(level = self.visibility_level) diff --git a/app/models/repository.rb b/app/models/repository.rb index 59f913c7fb4..9fb2e2aa306 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -206,12 +206,18 @@ class Repository end def branch_exists?(branch_name) - branch_names.include?(branch_name) + return false unless raw_repository + + @branch_exists_memo ||= Hash.new do |hash, key| + hash[key] = raw_repository.branch_exists?(key) + end + + @branch_exists_memo[branch_name] end def ref_exists?(ref) - rugged.references.exist?(ref) - rescue Rugged::ReferenceError + !!raw_repository&.ref_exists?(ref) + rescue ArgumentError false end @@ -266,6 +272,7 @@ class Repository def expire_branches_cache expire_method_caches(%i(branch_names branch_count)) @local_branches = nil + @branch_exists_memo = nil end def expire_statistics_caches diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb new file mode 100644 index 00000000000..8d793f5c02e --- /dev/null +++ b/app/services/groups/nested_create_service.rb @@ -0,0 +1,45 @@ +module Groups + class NestedCreateService < Groups::BaseService + attr_reader :group_path + + def initialize(user, params) + @current_user, @params = user, params.dup + + @group_path = @params.delete(:group_path) + end + + def execute + return nil unless group_path + + if group = Group.find_by_full_path(group_path) + return group + end + + create_group_path + end + + private + + def create_group_path + group_path_segments = group_path.split('/') + + last_group = nil + partial_path_segments = [] + while subgroup_name = group_path_segments.shift + partial_path_segments << subgroup_name + partial_path = partial_path_segments.join('/') + + new_params = params.reverse_merge( + path: subgroup_name, + name: subgroup_name, + parent: last_group + ) + new_params[:visibility_level] ||= Gitlab::CurrentSettings.current_application_settings.default_group_visibility + + last_group = Group.find_by_full_path(partial_path) || Groups::CreateService.new(current_user, new_params).execute + end + + last_group + end + end +end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 4a4f2b91182..1486db046b5 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -192,6 +192,8 @@ class IssuableBaseService < BaseService def after_create(issuable) # To be overridden by subclasses + + issuable.update_project_counter_caches end def before_update(issuable) @@ -200,6 +202,8 @@ class IssuableBaseService < BaseService def after_update(issuable) # To be overridden by subclasses + + issuable.update_project_counter_caches end def update(issuable) diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index 234fcbede03..0307634c0b6 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -27,6 +27,8 @@ module Issues todo_service.new_issue(issuable, current_user) user_agent_detail_service.create resolve_discussions_with_issue(issuable) + + super end def resolve_discussions_with_issue(issue) diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb index 194413bf321..3d53fe0646b 100644 --- a/app/services/merge_requests/create_service.rb +++ b/app/services/merge_requests/create_service.rb @@ -28,6 +28,8 @@ module MergeRequests todo_service.new_merge_request(issuable, current_user) issuable.cache_merge_request_closes_issues!(current_user) update_merge_requests_head_pipeline(issuable) + + super end private diff --git a/app/services/projects/count_service.rb b/app/services/projects/count_service.rb new file mode 100644 index 00000000000..5e633c37bf8 --- /dev/null +++ b/app/services/projects/count_service.rb @@ -0,0 +1,43 @@ +module Projects + # Base class for the various service classes that count project data (e.g. + # issues or forks). + class CountService + def initialize(project) + @project = project + end + + def relation_for_count + raise( + NotImplementedError, + '"relation_for_count" must be implemented and return an ActiveRecord::Relation' + ) + end + + def count + Rails.cache.fetch(cache_key) { uncached_count } + end + + def refresh_cache + Rails.cache.write(cache_key, uncached_count) + end + + def uncached_count + relation_for_count.count + end + + def delete_cache + Rails.cache.delete(cache_key) + end + + def cache_key_name + raise( + NotImplementedError, + '"cache_key_name" must be implemented and return a String' + ) + end + + def cache_key + ['projects', @project.id, cache_key_name] + end + end +end diff --git a/app/services/projects/forks_count_service.rb b/app/services/projects/forks_count_service.rb index e2e2b1da91d..3a0fa84b868 100644 --- a/app/services/projects/forks_count_service.rb +++ b/app/services/projects/forks_count_service.rb @@ -1,30 +1,12 @@ module Projects # Service class for getting and caching the number of forks of a project. - class ForksCountService - def initialize(project) - @project = project + class ForksCountService < CountService + def relation_for_count + @project.forks end - def count - Rails.cache.fetch(cache_key) { uncached_count } - end - - def refresh_cache - Rails.cache.write(cache_key, uncached_count) - end - - def delete_cache - Rails.cache.delete(cache_key) - end - - private - - def uncached_count - @project.forks.count - end - - def cache_key - ['projects', @project.id, 'forks_count'] + def cache_key_name + 'forks_count' end end end diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb new file mode 100644 index 00000000000..3c0d186a73c --- /dev/null +++ b/app/services/projects/open_issues_count_service.rb @@ -0,0 +1,15 @@ +module Projects + # Service class for counting and caching the number of open issues of a + # project. + class OpenIssuesCountService < CountService + def relation_for_count + # We don't include confidential issues in this number since this would + # expose the number of confidential issues to non project members. + @project.issues.opened.public_only + end + + def cache_key_name + 'open_issues_count' + end + end +end diff --git a/app/services/projects/open_merge_requests_count_service.rb b/app/services/projects/open_merge_requests_count_service.rb new file mode 100644 index 00000000000..2a90f78b90d --- /dev/null +++ b/app/services/projects/open_merge_requests_count_service.rb @@ -0,0 +1,13 @@ +module Projects + # Service class for counting and caching the number of open merge requests of + # a project. + class OpenMergeRequestsCountService < CountService + def relation_for_count + @project.merge_requests.opened + end + + def cache_key_name + 'open_merge_requests_count' + end + end +end diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index 487f1cf5c4f..ee87f25a225 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -1,25 +1,21 @@ - @no_container = true - page_title "Logs" -- loggers = [Gitlab::GitLogger, Gitlab::AppLogger, - Gitlab::EnvironmentLogger, Gitlab::SidekiqLogger, - Gitlab::RepositoryCheckLogger] = render 'admin/monitoring/head' %div{ class: container_class } %ul.nav-links.log-tabs - - loggers.each do |klass| - %li{ class: active_when(klass == Gitlab::GitLogger) }> - = link_to klass::file_name, "##{klass::file_name_noext}", - 'data-toggle' => 'tab' + - @loggers.each do |klass| + %li{ class: active_when(klass == @loggers.first) }> + = link_to klass.file_name, "##{klass.file_name_noext}", data: { toggle: 'tab' } .row-content-block To prevent performance issues admin logs output the last 2000 lines .tab-content - - loggers.each do |klass| - .tab-pane{ class: active_when(klass == Gitlab::GitLogger), id: klass::file_name_noext } + - @loggers.each do |klass| + .tab-pane{ class: active_when(klass == @loggers.first), id: klass.file_name_noext } .file-holder#README .js-file-title.file-title %i.fa.fa-file - = klass::file_name + = klass.file_name .pull-right = link_to '#', class: 'log-bottom' do %i.fa.fa-arrow-down diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml index bf655f9d21a..e3c5fd55f08 100644 --- a/app/views/events/_event_push.atom.haml +++ b/app/views/events/_event_push.atom.haml @@ -5,9 +5,10 @@ %i at = event.created_at.to_s(:short) - %blockquote= markdown(escape_once(event.commit_title), pipeline: :atom, project: event.project, author: event.author) - - if event.commits_count > 1 - %p - %i - \... and - = pluralize(event.commits_count - 1, "more commit") + - unless event.rm_ref? + %blockquote= markdown(escape_once(event.commit_title), pipeline: :atom, project: event.project, author: event.author) + - if event.commits_count > 1 + %p + %i + \... and + = pluralize(event.commits_count - 1, "more commit") diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index 973c652ad88..53ebdd6d2ff 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -41,7 +41,3 @@ %li.commits-stat = link_to create_mr_path(project.default_branch, event.ref_name, project) do Create Merge Request -- elsif event.rm_ref? - .event-body - %ul.well-list.event_commits - = render "events/commit", project: project, event: event diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml index 6d9ec043590..9382ee8715e 100644 --- a/app/views/layouts/errors.html.haml +++ b/app/views/layouts/errors.html.haml @@ -15,7 +15,7 @@ h1 { font-size: 56px; line-height: 100px; - font-weight: normal; + font-weight: 400; color: #456; } @@ -28,7 +28,7 @@ h3 { color: #456; font-size: 20px; - font-weight: normal; + font-weight: 400; line-height: 28px; } diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml index 0ef81375c3a..81c84756ff8 100644 --- a/app/views/layouts/nav/_new_project_sidebar.html.haml +++ b/app/views/layouts/nav/_new_project_sidebar.html.haml @@ -86,7 +86,8 @@ %span.nav-item-name Issues - if @project.issues_enabled? - %span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count) + %span.badge.count.issue_counter + = number_with_delimiter(@project.open_issues_count) %ul.sidebar-sub-level-items = nav_link(controller: :issues) do @@ -116,7 +117,8 @@ = custom_icon('mr_bold') %span.nav-item-name Merge Requests - %span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count) + %span.badge.count.merge_counter.js-merge-counter + = number_with_delimiter(@project.open_merge_requests_count) - if project_nav_tab? :pipelines = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 924cd2e9681..b88465848e3 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -28,7 +28,8 @@ %span Issues - if @project.issues_enabled? - %span.badge.count.issue_counter= number_with_delimiter(issuables_count_for_state(:issues, :opened, finder: IssuesFinder.new(current_user, project_id: @project.id))) + %span.badge.count.issue_counter + = number_with_delimiter(@project.open_issues_count) - if project_nav_tab? :merge_requests - controllers = [:merge_requests, 'projects/merge_requests/conflicts'] @@ -37,7 +38,8 @@ = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do %span Merge Requests - %span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(issuables_count_for_state(:merge_requests, :opened, finder: MergeRequestsFinder.new(current_user, project_id: @project.id))) + %span.badge.count.merge_counter.js-merge-counter + = number_with_delimiter(@project.open_merge_requests_count) - if project_nav_tab? :pipelines = nav_link(controller: [:pipelines, :builds, :environments, :artifacts]) do diff --git a/app/views/layouts/oauth_error.html.haml b/app/views/layouts/oauth_error.html.haml index 34bcd2a8b3a..03b387f8181 100644 --- a/app/views/layouts/oauth_error.html.haml +++ b/app/views/layouts/oauth_error.html.haml @@ -19,7 +19,7 @@ h3 { color: #456; font-size: 22px; - font-weight: bold; + font-weight: 600; margin-bottom: 6px; } diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml index 97cf13df070..5638b7da1b0 100644 --- a/app/views/projects/_project_templates.html.haml +++ b/app/views/projects/_project_templates.html.haml @@ -1,6 +1,6 @@ .project-templates-buttons.import-buttons{ data: { toggle: "buttons" } } .btn.blank-option.active - %input{ type: "radio", autocomplete: "off", name: "project_templates", id: "blank", checked: "true" } + %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: "blank", checked: "true", value: "" } = icon('file-o', class: 'btn-template-icon') Blank - Gitlab::ProjectTemplate.all.each do |template| diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 7e8a5a38086..1214aabe837 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -37,14 +37,14 @@ .commit-actions.hidden-xs - - if commit.status(ref) - = render_commit_status(commit, ref: ref) - - if request.xhr? = render partial: 'projects/commit/signature', object: commit.signature - else = render partial: 'projects/commit/ajax_signature', locals: { commit: commit } + - if commit.status(ref) + = render_commit_status(commit, ref: ref) + = link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent" = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard")) = link_to_browse_code(project, commit) diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index abc97bcdff5..25d862ab4de 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -8,7 +8,7 @@ - if runner.locked? = icon('lock', class: 'has-tooltip', title: 'Locked to current projects') - %small + %small.edit-runner = link_to edit_project_runner_path(@project, runner) do %i.fa.fa-edit.btn - else diff --git a/app/views/shared/issuable/_user_dropdown_item.html.haml b/app/views/shared/issuable/_user_dropdown_item.html.haml index c18e4975bb8..48d04678d47 100644 --- a/app/views/shared/issuable/_user_dropdown_item.html.haml +++ b/app/views/shared/issuable/_user_dropdown_item.html.haml @@ -4,7 +4,7 @@ %li.filter-dropdown-item{ class: ('js-current-user' if user == current_user) } %button.btn.btn-link.dropdown-user{ type: :button } .avatar-container.s40 - = user_avatar_without_link(user: user, lazy: avatar[:lazy], url: avatar[:url], size: 40).gsub('/images/{{avatar_url}}','{{avatar_url}}').html_safe + = user_avatar_without_link(user: user, lazy: avatar[:lazy], url: avatar[:url], size: 40, has_tooltip: false).gsub('/images/{{avatar_url}}','{{avatar_url}}').html_safe .dropdown-user-details %span = user.name diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index 8939aeb6c3a..80432a73e4e 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -15,8 +15,11 @@ = link_to filter_projects_path(archived: nil), class: ("is-active" unless params[:archived].present?) do Hide archived projects %li - = link_to filter_projects_path(archived: true), class: ("is-active" if params[:archived].present?) do + = link_to filter_projects_path(archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do Show archived projects + %li + = link_to filter_projects_path(archived: 'only'), class: ("is-active" if params[:archived] == 'only') do + Show archived projects only - if current_user %li.divider %li diff --git a/app/workers/namespaceless_project_destroy_worker.rb b/app/workers/namespaceless_project_destroy_worker.rb index a9073742ff7..1cfb0be759e 100644 --- a/app/workers/namespaceless_project_destroy_worker.rb +++ b/app/workers/namespaceless_project_destroy_worker.rb @@ -18,7 +18,8 @@ class NamespacelessProjectDestroyWorker rescue ActiveRecord::RecordNotFound return end - return unless project.namespace_id.nil? # Reject doing anything for projects that *do* have a namespace + + return if project.namespace # Reject doing anything for projects that *do* have a namespace project.team.truncate diff --git a/changelogs/unreleased/13719-git-gc-timeout.yml b/changelogs/unreleased/13719-git-gc-timeout.yml deleted file mode 100644 index 13cf97ec764..00000000000 --- a/changelogs/unreleased/13719-git-gc-timeout.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: "Raise Housekeeping timeout to 24 hours" -merge_request: 13719 diff --git a/changelogs/unreleased/35994-archived-projects-only.yml b/changelogs/unreleased/35994-archived-projects-only.yml new file mode 100644 index 00000000000..ce565b177d0 --- /dev/null +++ b/changelogs/unreleased/35994-archived-projects-only.yml @@ -0,0 +1,5 @@ +--- +title: Add an option to list only archived projects +merge_request: 13492 +author: Mehdi Lahmam (@mehlah) +type: added diff --git a/changelogs/unreleased/36262_merge_request_reference_in_merge_commit_global.yml b/changelogs/unreleased/36262_merge_request_reference_in_merge_commit_global.yml new file mode 100644 index 00000000000..356857d6e8a --- /dev/null +++ b/changelogs/unreleased/36262_merge_request_reference_in_merge_commit_global.yml @@ -0,0 +1,5 @@ +--- +title: Merge request reference in merge commit changed to full reference +merge_request: 13518 +author: haseebeqx +type: fixed diff --git a/changelogs/unreleased/backstage-gb-after-save-asynchronous-job-hooks.yml b/changelogs/unreleased/backstage-gb-after-save-asynchronous-job-hooks.yml new file mode 100644 index 00000000000..fd0b7c4f43c --- /dev/null +++ b/changelogs/unreleased/backstage-gb-after-save-asynchronous-job-hooks.yml @@ -0,0 +1,5 @@ +--- +title: Fire hooks asynchronously when creating a new job to improve performance +merge_request: 13734 +author: +type: changed diff --git a/changelogs/unreleased/bvl-improve-bare-project-import.yml b/changelogs/unreleased/bvl-improve-bare-project-import.yml new file mode 100644 index 00000000000..74c1da4ea40 --- /dev/null +++ b/changelogs/unreleased/bvl-improve-bare-project-import.yml @@ -0,0 +1,6 @@ +--- +title: 'Improve bare project import: Allow subgroups, take default visibility level + into account' +merge_request: 13670 +author: +type: fixed diff --git a/changelogs/unreleased/cache-issue-and-mr-counts.yml b/changelogs/unreleased/cache-issue-and-mr-counts.yml new file mode 100644 index 00000000000..fe3fe3be976 --- /dev/null +++ b/changelogs/unreleased/cache-issue-and-mr-counts.yml @@ -0,0 +1,5 @@ +--- +title: Cache the number of open issues and merge requests +merge_request: +author: +type: other diff --git a/changelogs/unreleased/dm-commit-cache-i18n.yml b/changelogs/unreleased/dm-commit-cache-i18n.yml deleted file mode 100644 index d47226fd408..00000000000 --- a/changelogs/unreleased/dm-commit-cache-i18n.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Commit rows would occasionally render with the wrong language -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/dm-ldap-adapter-attributes.yml b/changelogs/unreleased/dm-ldap-adapter-attributes.yml new file mode 100644 index 00000000000..edd68ef08e7 --- /dev/null +++ b/changelogs/unreleased/dm-ldap-adapter-attributes.yml @@ -0,0 +1,6 @@ +--- +title: Fix signing in using LDAP when attribute mapping uses simple strings instead + of arrays +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/fix-broadcast-message-caching.yml b/changelogs/unreleased/fix-broadcast-message-caching.yml deleted file mode 100644 index 58ec1766cfd..00000000000 --- a/changelogs/unreleased/fix-broadcast-message-caching.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix caching of future broadcast messages -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/fix-gb-fix-head-pipeline-when-pipeline-has-errors.yml b/changelogs/unreleased/fix-gb-fix-head-pipeline-when-pipeline-has-errors.yml deleted file mode 100644 index ede8031a501..00000000000 --- a/changelogs/unreleased/fix-gb-fix-head-pipeline-when-pipeline-has-errors.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix merge request pipeline status when pipeline has errors -merge_request: 13664 -author: -type: fixed diff --git a/changelogs/unreleased/fix-old-mr-diffs.yml b/changelogs/unreleased/fix-old-mr-diffs.yml new file mode 100644 index 00000000000..b0a011cf354 --- /dev/null +++ b/changelogs/unreleased/fix-old-mr-diffs.yml @@ -0,0 +1,6 @@ +--- +title: Show un-highlighted text diffs when we do not have references to the correct + blobs +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/fix-push-events-branch-removals.yml b/changelogs/unreleased/fix-push-events-branch-removals.yml new file mode 100644 index 00000000000..71f368db296 --- /dev/null +++ b/changelogs/unreleased/fix-push-events-branch-removals.yml @@ -0,0 +1,5 @@ +--- +title: Fix display of push events for removed refs +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/font-weight-adjusted.yml b/changelogs/unreleased/font-weight-adjusted.yml new file mode 100644 index 00000000000..827f3485099 --- /dev/null +++ b/changelogs/unreleased/font-weight-adjusted.yml @@ -0,0 +1,5 @@ +--- +title: Changed all font-weight values to 400 and 600 and introduced 2 variables to + manage them +merge_request: !12896 +author: diff --git a/changelogs/unreleased/only-limit-fetch-when-requested.yml b/changelogs/unreleased/only-limit-fetch-when-requested.yml deleted file mode 100644 index d9acdf56511..00000000000 --- a/changelogs/unreleased/only-limit-fetch-when-requested.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Only require Sidekiq throttling library when enabled, to reduce cache misses -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/rd-fix-broken-configuration-for-some-integrations.yml b/changelogs/unreleased/rd-fix-broken-configuration-for-some-integrations.yml new file mode 100644 index 00000000000..00528397d64 --- /dev/null +++ b/changelogs/unreleased/rd-fix-broken-configuration-for-some-integrations.yml @@ -0,0 +1,5 @@ +--- +title: Testing of some integrations were broken due to missing ServiceHook record. +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/tc-remove-nonexisting-namespace-pending-delete-projects.yml b/changelogs/unreleased/tc-remove-nonexisting-namespace-pending-delete-projects.yml new file mode 100644 index 00000000000..218336df5d2 --- /dev/null +++ b/changelogs/unreleased/tc-remove-nonexisting-namespace-pending-delete-projects.yml @@ -0,0 +1,5 @@ +--- +title: Migration to remove pending delete projects with non-existing namespace +merge_request: 13598 +author: +type: other diff --git a/changelogs/unreleased/use_full_path_in_project_avatar_url_webhook.yml b/changelogs/unreleased/use_full_path_in_project_avatar_url_webhook.yml new file mode 100644 index 00000000000..0c3acce1455 --- /dev/null +++ b/changelogs/unreleased/use_full_path_in_project_avatar_url_webhook.yml @@ -0,0 +1,5 @@ +--- +title: Use full path of project's avatar in webhooks +merge_request: 13649 +author: Vitaliy @blackst0ne Klachkov +type: changed diff --git a/changelogs/unreleased/zj-add-pipeline-source-variable.yml b/changelogs/unreleased/zj-add-pipeline-source-variable.yml new file mode 100644 index 00000000000..5d98cd8086a --- /dev/null +++ b/changelogs/unreleased/zj-add-pipeline-source-variable.yml @@ -0,0 +1,5 @@ +--- +title: Add CI_PIPELINE_SOURCE variable on CI Jobs +merge_request: +author: +type: added diff --git a/changelogs/unreleased/zj-fix-fe-blank-button.yml b/changelogs/unreleased/zj-fix-fe-blank-button.yml new file mode 100644 index 00000000000..2165d4186c1 --- /dev/null +++ b/changelogs/unreleased/zj-fix-fe-blank-button.yml @@ -0,0 +1,5 @@ +--- +title: Fix new project form not resetting the template value +merge_request: +author: +type: fixed diff --git a/db/post_migrate/20170816102555_cleanup_nonexisting_namespace_pending_delete_projects.rb b/db/post_migrate/20170816102555_cleanup_nonexisting_namespace_pending_delete_projects.rb new file mode 100644 index 00000000000..3f085c17133 --- /dev/null +++ b/db/post_migrate/20170816102555_cleanup_nonexisting_namespace_pending_delete_projects.rb @@ -0,0 +1,54 @@ +# Follow up of CleanupNamespacelessPendingDeleteProjects and it cleans +# all projects with `pending_delete = true` and for which the +# namespace no longer exists. +class CleanupNonexistingNamespacePendingDeleteProjects < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + class Project < ActiveRecord::Base + self.table_name = 'projects' + + include ::EachBatch + end + + class Namespace < ActiveRecord::Base + self.table_name = 'namespaces' + end + + def up + find_projects.each_batch do |batch| + args = batch.pluck(:id).map { |id| [id] } + + NamespacelessProjectDestroyWorker.bulk_perform_async(args) + end + end + + def down + # NOOP + end + + private + + def find_projects + projects = Project.arel_table + namespaces = Namespace.arel_table + + namespace_query = namespaces.project(1) + .where(namespaces[:id].eq(projects[:namespace_id])) + .exists.not + + # SELECT "projects"."id" + # FROM "projects" + # WHERE "projects"."pending_delete" = 't' + # AND (NOT (EXISTS + # (SELECT 1 + # FROM "namespaces" + # WHERE "namespaces"."id" = "projects"."namespace_id"))) + Project.where(projects[:pending_delete].eq(true)) + .where(namespace_query) + .select(:id) + end +end diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 22e7f6879ed..e55a92dbb71 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -57,6 +57,7 @@ future GitLab releases.** | **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags | | **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally | | **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] | +| **CI_PIPELINE_SOURCE** | 10.0 | all | The source for this pipeline, one of: push, web, trigger, schedule, api, external. Pipelines created before 9.5 will have unknown as source | | **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run | | **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally | | **CI_PROJECT_NAME** | 8.10 | 0.5 | The project name that is currently being built (actually it is project folder name) | diff --git a/doc/development/licensing.md b/doc/development/licensing.md index 1f115059fb8..2b16dfe0e7c 100644 --- a/doc/development/licensing.md +++ b/doc/development/licensing.md @@ -66,7 +66,7 @@ Libraries with the following licenses are unacceptable for use: ## Requesting Approval for Licenses -Libraries that are not listed in the [Acceptable Licenses][Acceptable-Licenses] or [Unacceptable Licenses][Unacceptable-Licenses] list can be submitted to the legal team for review. Please create an issue in the [Organization Repository][Org-Repo] and cc `@gl-legal`. After a decision has been made, the original requestor is responsible for updating this document. +Libraries that are not listed in the [Acceptable Licenses][Acceptable-Licenses] or [Unacceptable Licenses][Unacceptable-Licenses] list can be submitted to the legal team for review. Please email `legal@gitlab.com` with the details. After a decision has been made, the original requestor is responsible for updating this document. ## Notes diff --git a/features/steps/project/pages.rb b/features/steps/project/pages.rb index 275fb4fc010..bb69c0d6e99 100644 --- a/features/steps/project/pages.rb +++ b/features/steps/project/pages.rb @@ -35,7 +35,10 @@ class Spinach::Features::ProjectPages < Spinach::FeatureSteps end step 'pages are deployed' do - pipeline = @project.pipelines.create(ref: 'HEAD', sha: @project.commit('HEAD').sha) + pipeline = @project.pipelines.create(ref: 'HEAD', + sha: @project.commit('HEAD').sha, + source: :push) + build = build(:ci_build, project: @project, pipeline: pipeline, @@ -43,6 +46,7 @@ class Spinach::Features::ProjectPages < Spinach::FeatureSteps artifacts_file: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip'), artifacts_metadata: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta') ) + result = ::Projects::UpdatePagesService.new(@project, build).execute expect(result[:status]).to eq(:success) end diff --git a/lib/github/import.rb b/lib/github/import.rb index 4cc01593ef4..7b848081e85 100644 --- a/lib/github/import.rb +++ b/lib/github/import.rb @@ -107,7 +107,7 @@ module Github # this means that repo has wiki enabled, but have no pages. So, # we can skip the import. if e.message !~ /repository not exported/ - errors(:wiki, wiki_url, e.message) + error(:wiki, wiki_url, e.message) end end diff --git a/lib/gitlab/bare_repository_importer.rb b/lib/gitlab/bare_repository_importer.rb new file mode 100644 index 00000000000..9323bfc7fb2 --- /dev/null +++ b/lib/gitlab/bare_repository_importer.rb @@ -0,0 +1,96 @@ +module Gitlab + class BareRepositoryImporter + NoAdminError = Class.new(StandardError) + + def self.execute + Gitlab.config.repositories.storages.each do |storage_name, repository_storage| + git_base_path = repository_storage['path'] + repos_to_import = Dir.glob(git_base_path + '/**/*.git') + + repos_to_import.each do |repo_path| + if repo_path.end_with?('.wiki.git') + log " * Skipping wiki repo" + next + end + + log "Processing #{repo_path}".color(:yellow) + + repo_relative_path = repo_path[repository_storage['path'].length..-1] + .sub(/^\//, '') # Remove leading `/` + .sub(/\.git$/, '') # Remove `.git` at the end + new(storage_name, repo_relative_path).create_project_if_needed + end + end + end + + attr_reader :storage_name, :full_path, :group_path, :project_path, :user + delegate :log, to: :class + + def initialize(storage_name, repo_path) + @storage_name = storage_name + @full_path = repo_path + + unless @user = User.admins.order_id_asc.first + raise NoAdminError.new('No admin user found to import repositories') + end + + @group_path, @project_path = File.split(repo_path) + @group_path = nil if @group_path == '.' + end + + def create_project_if_needed + if project = Project.find_by_full_path(full_path) + log " * #{project.name} (#{full_path}) exists" + return project + end + + create_project + end + + private + + def create_project + group = find_or_create_group + + project_params = { + name: project_path, + path: project_path, + repository_storage: storage_name, + namespace_id: group&.id + } + + project = Projects::CreateService.new(user, project_params).execute + + if project.persisted? + log " * Created #{project.name} (#{full_path})".color(:green) + ProjectCacheWorker.perform_async(project.id) + else + log " * Failed trying to create #{project.name} (#{full_path})".color(:red) + log " Errors: #{project.errors.messages}".color(:red) + end + + project + end + + def find_or_create_group + return nil unless group_path + + if namespace = Namespace.find_by_full_path(group_path) + log " * Namespace #{group_path} exists.".color(:green) + return namespace + end + + log " * Creating Group: #{group_path}" + Groups::NestedCreateService.new(user, group_path: group_path).execute + end + + # This is called from within a rake task only used by Admins, so allow writing + # to STDOUT + # + # rubocop:disable Rails/Output + def self.log(message) + puts message + end + # rubocop:enable Rails/Output + end +end diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index 6d7de52cb80..1dabd4ebdd0 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -186,7 +186,10 @@ module Gitlab end def content_changed? - old_blob && new_blob && old_blob.id != new_blob.id + return blobs_changed? if diff_refs + return false if new_file? || deleted_file? || renamed_file? + + text? && diff_lines.any? end def different_type? @@ -225,6 +228,10 @@ module Gitlab private + def blobs_changed? + old_blob && new_blob && old_blob.id != new_blob.id + end + def simple_viewer_class return DiffViewer::NotDiffable unless diffable? @@ -250,6 +257,8 @@ module Gitlab DiffViewer::Renamed elsif mode_changed? DiffViewer::ModeChanged + else + DiffViewer::NoPreview end end diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index a499bbc6266..5ee6669050c 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -271,7 +271,13 @@ module Gitlab # # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/324 def to_diff - rugged_diff_from_parent.patch + Gitlab::GitalyClient.migrate(:commit_patch) do |is_enabled| + if is_enabled + @repository.gitaly_commit_client.patch(id) + else + rugged_diff_from_parent.patch + end + end end # Returns a diff object for the changes from this commit's first parent. diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 976ea7e4035..b835dec24eb 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -154,7 +154,7 @@ module Gitlab if is_enabled gitaly_ref_client.count_branch_names else - rugged.branches.count do |ref| + rugged.branches.each(:local).count do |ref| begin ref.name && ref.target # ensures the branch is valid @@ -202,6 +202,19 @@ module Gitlab end end + # Returns true if the given ref name exists + # + # Ref names must start with `refs/`. + def ref_exists?(ref_name) + gitaly_migrate(:ref_exists) do |is_enabled| + if is_enabled + gitaly_ref_exists?(ref_name) + else + rugged_ref_exists?(ref_name) + end + end + end + # Returns true if the given tag exists # # name - The name of the tag as a String. @@ -993,6 +1006,16 @@ module Gitlab # Returns true if the given ref name exists # # Ref names must start with `refs/`. + def rugged_ref_exists?(ref_name) + raise ArgumentError, 'invalid refname' unless ref_name.start_with?('refs/') + rugged.references.exist?(ref_name) + rescue Rugged::ReferenceError + false + end + + # Returns true if the given ref name exists + # + # Ref names must start with `refs/`. def gitaly_ref_exists?(ref_name) gitaly_ref_client.ref_exists?(ref_name) end diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index 2d58fb0186e..57f42bd35ee 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -175,8 +175,8 @@ module Gitlab def raw_blame(revision, path) request = Gitaly::RawBlameRequest.new( repository: @gitaly_repo, - revision: revision, - path: path + revision: GitalyClient.encode(revision), + path: GitalyClient.encode(path) ) response = GitalyClient.call(@repository.storage, :commit_service, :raw_blame, request) @@ -194,6 +194,16 @@ module Gitlab response.commit end + def patch(revision) + request = Gitaly::CommitPatchRequest.new( + repository: @gitaly_repo, + revision: GitalyClient.encode(revision) + ) + response = GitalyClient.call(@repository.storage, :diff_service, :commit_patch, request) + + response.sum(&:data) + end + private def commit_diff_request_params(commit, options = {}) diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb index cdcfed36740..8c0008c6971 100644 --- a/lib/gitlab/gitaly_client/ref_service.rb +++ b/lib/gitlab/gitaly_client/ref_service.rb @@ -71,7 +71,7 @@ module Gitlab end def ref_exists?(ref_name) - request = Gitaly::RefExistsRequest.new(repository: @gitaly_repo, ref: ref_name) + request = Gitaly::RefExistsRequest.new(repository: @gitaly_repo, ref: GitalyClient.encode(ref_name)) response = GitalyClient.call(@storage, :ref_service, :ref_exists, request) response.value rescue GRPC::InvalidArgument => e diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb index 8867a91c244..cd7e4ca7b7e 100644 --- a/lib/gitlab/ldap/adapter.rb +++ b/lib/gitlab/ldap/adapter.rb @@ -73,7 +73,7 @@ module Gitlab private def user_options(field, value, limit) - options = { attributes: user_attributes } + options = { attributes: Gitlab::LDAP::Person.ldap_attributes(config).compact.uniq } options[:size] = limit if limit if field.to_sym == :dn @@ -99,10 +99,6 @@ module Gitlab filter end end - - def user_attributes - %W(#{config.uid} cn dn) + config.attributes['username'] + config.attributes['email'] - end end end end diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb index e138b466a34..4d6f8ac79de 100644 --- a/lib/gitlab/ldap/person.rb +++ b/lib/gitlab/ldap/person.rb @@ -21,6 +21,15 @@ module Gitlab adapter.dn_matches_filter?(dn, AD_USER_DISABLED) end + def self.ldap_attributes(config) + [ + 'dn', # Used in `dn` + config.uid, # Used in `uid` + *config.attributes['name'], # Used in `name` + *config.attributes['email'] # Used in `email` + ] + end + def initialize(entry, provider) Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" } @entry = entry diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb index 59b21149a9a..6bffd410ed0 100644 --- a/lib/gitlab/logger.rb +++ b/lib/gitlab/logger.rb @@ -14,13 +14,9 @@ module Gitlab def self.read_latest path = Rails.root.join("log", file_name) - self.build unless File.exist?(path) - tail_output, _ = Gitlab::Popen.popen(%W(tail -n 2000 #{path})) - tail_output.split("\n") - end - def self.read_latest_for(filename) - path = Rails.root.join("log", filename) + return [] unless File.readable?(path) + tail_output, _ = Gitlab::Popen.popen(%W(tail -n 2000 #{path})) tail_output.split("\n") end diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake index 6e10ba374bf..d227a0c8bdb 100644 --- a/lib/tasks/gitlab/import.rake +++ b/lib/tasks/gitlab/import.rake @@ -9,6 +9,7 @@ namespace :gitlab do # * The project owner will set to the first administator of the system # * Existing projects will be skipped # + # desc "GitLab | Import bare repositories from repositories -> storages into GitLab project instance" task repos: :environment do if Project.current_application_settings.hashed_storage_enabled @@ -17,69 +18,7 @@ namespace :gitlab do exit 1 end - Gitlab.config.repositories.storages.each_value do |repository_storage| - git_base_path = repository_storage['path'] - repos_to_import = Dir.glob(git_base_path + '/**/*.git') - - repos_to_import.each do |repo_path| - # strip repo base path - repo_path[0..git_base_path.length] = '' - - path = repo_path.sub(/\.git$/, '') - group_name, name = File.split(path) - group_name = nil if group_name == '.' - - puts "Processing #{repo_path}".color(:yellow) - - if path.end_with?('.wiki') - puts " * Skipping wiki repo" - next - end - - project = Project.find_by_full_path(path) - - if project - puts " * #{project.name} (#{repo_path}) exists" - else - user = User.admins.reorder("id").first - - project_params = { - name: name, - path: name - } - - # find group namespace - if group_name - group = Namespace.find_by(path: group_name) - # create group namespace - unless group - group = Group.new(name: group_name) - group.path = group_name - group.owner = user - if group.save - puts " * Created Group #{group.name} (#{group.id})".color(:green) - else - puts " * Failed trying to create group #{group.name}".color(:red) - end - end - # set project group - project_params[:namespace_id] = group.id - end - - project = Projects::CreateService.new(user, project_params).execute - - if project.persisted? - puts " * Created #{project.name} (#{repo_path})".color(:green) - ProjectCacheWorker.perform_async(project.id) - else - puts " * Failed trying to create #{project.name} (#{repo_path})".color(:red) - puts " Errors: #{project.errors.messages}".color(:red) - end - end - end - end - - puts "Done!".color(:green) + Gitlab::BareRepositoryImporter.execute end end end diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake index 96b8f59242c..1206302cb76 100644 --- a/lib/tasks/import.rake +++ b/lib/tasks/import.rake @@ -72,23 +72,7 @@ class GithubImport return @current_user.namespace if names == @current_user.namespace_path return @current_user.namespace unless @current_user.can_create_group? - full_path_namespace = Namespace.find_by_full_path(names) - - return full_path_namespace if full_path_namespace - - names.split('/').inject(nil) do |parent, name| - begin - namespace = Group.create!(name: name, - path: name, - owner: @current_user, - parent: parent) - namespace.add_owner(@current_user) - - namespace - rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid - Namespace.where(parent: parent).find_by_path_or_name(name) - end - end + Groups::NestedCreateService.new(@current_user, group_path: names).execute end def full_path_namespace(names) diff --git a/public/404.html b/public/404.html index 03e98e81862..4db72be6f8c 100644 --- a/public/404.html +++ b/public/404.html @@ -15,7 +15,7 @@ h1 { font-size: 56px; line-height: 100px; - font-weight: normal; + font-weight: 400; color: #456; } @@ -28,7 +28,7 @@ h3 { color: #456; font-size: 20px; - font-weight: normal; + font-weight: 400; line-height: 28px; } @@ -48,7 +48,7 @@ a { line-height: 100px; - font-weight: normal; + font-weight: 400; color: #4A8BEE; font-size: 18px; text-decoration: none; diff --git a/public/422.html b/public/422.html index 49ebbe40f39..a67dcd02200 100644 --- a/public/422.html +++ b/public/422.html @@ -15,7 +15,7 @@ h1 { font-size: 56px; line-height: 100px; - font-weight: normal; + font-weight: 400; color: #456; } @@ -28,7 +28,7 @@ h3 { color: #456; font-size: 20px; - font-weight: normal; + font-weight: 400; line-height: 28px; } @@ -48,7 +48,7 @@ a { line-height: 100px; - font-weight: normal; + font-weight: 400; color: #4A8BEE; font-size: 18px; text-decoration: none; diff --git a/public/500.html b/public/500.html index 516920f7471..7091d14dfc4 100644 --- a/public/500.html +++ b/public/500.html @@ -15,7 +15,7 @@ h1 { font-size: 56px; line-height: 100px; - font-weight: normal; + font-weight: 400; color: #456; } @@ -28,7 +28,7 @@ h3 { color: #456; font-size: 20px; - font-weight: normal; + font-weight: 400; line-height: 28px; } @@ -48,7 +48,7 @@ a { line-height: 100px; - font-weight: normal; + font-weight: 400; color: #4A8BEE; font-size: 18px; text-decoration: none; diff --git a/public/502.html b/public/502.html index 189458c9816..82afd273248 100644 --- a/public/502.html +++ b/public/502.html @@ -15,7 +15,7 @@ h1 { font-size: 56px; line-height: 100px; - font-weight: normal; + font-weight: 400; color: #456; } @@ -28,7 +28,7 @@ h3 { color: #456; font-size: 20px; - font-weight: normal; + font-weight: 400; line-height: 28px; } @@ -48,7 +48,7 @@ a { line-height: 100px; - font-weight: normal; + font-weight: 400; color: #4A8BEE; font-size: 18px; text-decoration: none; diff --git a/public/503.html b/public/503.html index b09b0e2a67e..f1486bc3e84 100644 --- a/public/503.html +++ b/public/503.html @@ -15,7 +15,7 @@ h1 { font-size: 56px; line-height: 100px; - font-weight: normal; + font-weight: 400; color: #456; } @@ -28,7 +28,7 @@ h3 { color: #456; font-size: 20px; - font-weight: normal; + font-weight: 400; line-height: 28px; } @@ -48,7 +48,7 @@ a { line-height: 100px; - font-weight: normal; + font-weight: 400; color: #4A8BEE; font-size: 18px; text-decoration: none; diff --git a/public/deploy.html b/public/deploy.html index 49ec4ac5ce1..e463b62520c 100644 --- a/public/deploy.html +++ b/public/deploy.html @@ -20,7 +20,7 @@ h1 { font-size: 56px; line-height: 100px; - font-weight: normal; + font-weight: 400; color: #456; } @@ -33,7 +33,7 @@ h3 { color: #456; font-size: 20px; - font-weight: normal; + font-weight: 400; line-height: 28px; } @@ -66,4 +66,4 @@ <p>Please contact your GitLab administrator if this problem persists.</p> </div> </body> -</html>
\ No newline at end of file +</html> diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index 4e9b0c09ff2..efba9cc7306 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -10,9 +10,6 @@ describe Projects::ServicesController do before do sign_in(user) project.team << [user, :master] - - controller.instance_variable_set(:@project, project) - controller.instance_variable_set(:@service, service) end describe '#test' do @@ -20,7 +17,7 @@ describe Projects::ServicesController do it 'renders 404' do allow_any_instance_of(Service).to receive(:can_test?).and_return(false) - put :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id + put :test, namespace_id: project.namespace, project_id: project, id: service.to_param expect(response).to have_http_status(404) end @@ -36,7 +33,7 @@ describe Projects::ServicesController do it 'returns success' do allow_any_instance_of(MicrosoftTeams::Notifier).to receive(:ping).and_return(true) - put :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id + put :test, namespace_id: project.namespace, project_id: project, id: service.to_param expect(response.status).to eq(200) end @@ -45,7 +42,7 @@ describe Projects::ServicesController do it 'returns success' do expect(HipChat::Client).to receive(:new).with('hipchat_token_p', anything).and_return(hipchat_client) - put :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, service: service_params + put :test, namespace_id: project.namespace, project_id: project, id: service.to_param, service: service_params expect(response.status).to eq(200) end @@ -54,17 +51,42 @@ describe Projects::ServicesController do it 'returns success' do expect(HipChat::Client).to receive(:new).with('hipchat_token_p', anything).and_return(hipchat_client) - put :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, service: service_params + put :test, namespace_id: project.namespace, project_id: project, id: service.to_param, service: service_params expect(response.status).to eq(200) end + + context 'when service is configured for the first time' do + before do + allow_any_instance_of(ServiceHook).to receive(:execute).and_return(true) + end + + it 'persist the object' do + do_put + + expect(BuildkiteService.first).to be_present + end + + it 'creates the ServiceHook object' do + do_put + + expect(BuildkiteService.first.service_hook).to be_present + end + + def do_put + put :test, namespace_id: project.namespace, + project_id: project, + id: 'buildkite', + service: { 'active' => '1', 'push_events' => '1', token: 'token', 'project_url' => 'http://test.com' } + end + end end context 'failure' do it 'returns success status code and the error message' do expect(HipChat::Client).to receive(:new).with('hipchat_token_p', anything).and_raise('Bad test') - put :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, service: service_params + put :test, namespace_id: project.namespace, project_id: project, id: service.to_param, service: service_params expect(response.status).to eq(200) expect(JSON.parse(response.body)) @@ -77,7 +99,7 @@ describe Projects::ServicesController do context 'when param `active` is set to true' do it 'activates the service and redirects to integrations paths' do put :update, - namespace_id: project.namespace.id, project_id: project.id, id: service.id, service: { active: true } + namespace_id: project.namespace, project_id: project, id: service.to_param, service: { active: true } expect(response).to redirect_to(project_settings_integrations_path(project)) expect(flash[:notice]).to eq 'HipChat activated.' @@ -87,7 +109,7 @@ describe Projects::ServicesController do context 'when param `active` is set to false' do it 'does not activate the service but saves the settings' do put :update, - namespace_id: project.namespace.id, project_id: project.id, id: service.id, service: { active: false } + namespace_id: project.namespace, project_id: project, id: service.to_param, service: { active: false } expect(flash[:notice]).to eq 'HipChat settings saved, but not activated.' end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 77710f80036..f4f2505d436 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -36,6 +36,14 @@ describe "Admin::Projects" do expect(page).to have_content(archived_project.name) expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived') end + + it 'renders only archived projects', js: true do + find(:css, '#sort-projects-dropdown').click + click_link 'Show archived projects only' + + expect(page).to have_content(archived_project.name) + expect(page).not_to have_content(project.name) + end end describe "GET /admin/projects/:namespace_id/:id" do diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index 9a597a2d690..4fc6956d111 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' feature 'Contributions Calendar', :js do let(:user) { create(:user) } - let(:contributed_project) { create(:project, :public) } + let(:contributed_project) { create(:project, :public, :repository) } let(:issue_note) { create(:note, project: contributed_project) } # Ex/ Sunday Jan 1, 2016 diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb index 582868bac1e..bd115785646 100644 --- a/spec/features/dashboard/activity_spec.rb +++ b/spec/features/dashboard/activity_spec.rb @@ -17,7 +17,7 @@ feature 'Dashboard > Activity' do end context 'event filters', :js do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) do create(:merge_request, author: user, source_project: project, target_project: project) diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb index 814ec0e59c7..e8d699ff5e0 100644 --- a/spec/features/dashboard/archived_projects_spec.rb +++ b/spec/features/dashboard/archived_projects_spec.rb @@ -26,6 +26,13 @@ RSpec.describe 'Dashboard Archived Project' do expect(page).to have_link(archived_project.name) end + it 'renders only archived projects' do + click_link 'Show archived projects only' + + expect(page).to have_content(archived_project.name) + expect(page).not_to have_content(project.name) + end + it 'searchs archived projects', :js do click_button 'Last updated' click_link 'Show archived projects' diff --git a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb index 429bc277d73..08a3bb84aac 100644 --- a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb +++ b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb @@ -19,7 +19,7 @@ feature 'Clicking toggle commit message link', js: true do "Merge branch 'feature' into 'master'", merge_request.title, "Closes #{issue_1.to_reference} and #{issue_2.to_reference}", - "See merge request #{merge_request.to_reference}" + "See merge request #{merge_request.to_reference(full: true)}" ].join("\n\n") end let(:message_with_description) do @@ -27,7 +27,7 @@ feature 'Clicking toggle commit message link', js: true do "Merge branch 'feature' into 'master'", merge_request.title, merge_request.description, - "See merge request #{merge_request.to_reference}" + "See merge request #{merge_request.to_reference(full: true)}" ].join("\n\n") end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index cac31c34ad1..785cfeb34bd 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -1,149 +1,135 @@ require 'spec_helper' -describe "Runners" do - let(:user) { create(:user) } +feature 'Runners' do + given(:user) { create(:user) } - before do + background do sign_in(user) end - describe "specific runners" do - before do - @project = FactoryGirl.create :project, shared_runners_enabled: false - @project.team << [user, :master] + context 'when a project has enabled shared_runners' do + given(:project) { create(:project) } - @project2 = FactoryGirl.create :project - @project2.team << [user, :master] + background do + project.add_master(user) + end - @project3 = FactoryGirl.create :project - @project3.team << [user, :developer] + context 'when a specific runner is activated on the project' do + given(:specific_runner) { create(:ci_runner, :specific) } - @shared_runner = FactoryGirl.create :ci_runner, :shared - @specific_runner = FactoryGirl.create :ci_runner - @specific_runner2 = FactoryGirl.create :ci_runner - @specific_runner3 = FactoryGirl.create :ci_runner - @project.runners << @specific_runner - @project2.runners << @specific_runner2 - @project3.runners << @specific_runner3 + background do + project.runners << specific_runner + end - visit runners_path(@project) - end + scenario 'user sees the specific runner' do + visit runners_path(project) - before do - expect(page).not_to have_content(@specific_runner3.display_name) - expect(page).not_to have_content(@specific_runner3.display_name) - end + within '.activated-specific-runners' do + expect(page).to have_content(specific_runner.display_name) + end - it "places runners in right places" do - expect(page.find(".available-specific-runners")).to have_content(@specific_runner2.display_name) - expect(page.find(".activated-specific-runners")).to have_content(@specific_runner.display_name) - expect(page.find(".available-shared-runners")).to have_content(@shared_runner.display_name) - end + click_on specific_runner.short_sha - it "enables specific runner for project" do - within ".available-specific-runners" do - click_on "Enable for this project" + expect(page).to have_content(specific_runner.platform) end - expect(page.find(".activated-specific-runners")).to have_content(@specific_runner2.display_name) - end + scenario 'user removes an activated specific runner if this is last project for that runners' do + visit runners_path(project) - it "disables specific runner for project" do - @project2.runners << @specific_runner - visit runners_path(@project) + within '.activated-specific-runners' do + click_on 'Remove Runner' + end - within ".activated-specific-runners" do - click_on "Disable for this project" + expect(page).not_to have_content(specific_runner.display_name) end - expect(page.find(".available-specific-runners")).to have_content(@specific_runner.display_name) - end + context 'when a runner has a tag' do + background do + specific_runner.update(tag_list: ['tag']) + end - it "removes specific runner for project if this is last project for that runners" do - within ".activated-specific-runners" do - click_on "Remove Runner" - end + scenario 'user edits runner not to run untagged jobs' do + visit runners_path(project) - expect(Ci::Runner.exists?(id: @specific_runner)).to be_falsey - end - end + within '.activated-specific-runners' do + first('.edit-runner > a').click + end - describe "shared runners" do - before do - @project = FactoryGirl.create :project, shared_runners_enabled: false - @project.team << [user, :master] - visit runners_path(@project) - end + expect(page.find_field('runner[run_untagged]')).to be_checked - it "enables shared runners" do - click_on "Enable shared Runners" - expect(@project.reload.shared_runners_enabled).to be_truthy - end - end + uncheck 'runner_run_untagged' + click_button 'Save changes' - describe "shared runners description" do - let(:shared_runners_text) { 'custom **shared** runners description' } - let(:shared_runners_html) { 'custom shared runners description' } + expect(page).to have_content 'Can run untagged jobs No' + end + end - before do - stub_application_setting(shared_runners_text: shared_runners_text) - project = FactoryGirl.create :project, shared_runners_enabled: false - project.team << [user, :master] - visit runners_path(project) - end + context 'when a shared runner is activated on the project' do + given!(:shared_runner) { create(:ci_runner, :shared) } - it "sees shared runners description" do - expect(page.find(".shared-runners-description")).to have_content(shared_runners_html) - end - end + scenario 'user sees CI/CD setting page' do + visit runners_path(project) - describe "show page" do - before do - @project = FactoryGirl.create :project - @project.team << [user, :master] - @specific_runner = FactoryGirl.create :ci_runner - @project.runners << @specific_runner + expect(page.find('.available-shared-runners')).to have_content(shared_runner.display_name) + end + end end - it "shows runner information" do - visit runners_path(@project) - click_on @specific_runner.short_sha - expect(page).to have_content(@specific_runner.platform) - end - end + context 'when a specific runner exists in another project' do + given(:another_project) { create(:project) } + given(:specific_runner) { create(:ci_runner, :specific) } - feature 'configuring runners ability to picking untagged jobs' do - given(:project) { create(:project) } - given(:runner) { create(:ci_runner) } + background do + another_project.add_master(user) + another_project.runners << specific_runner + end - background do - project.team << [user, :master] - project.runners << runner - end + scenario 'user enables and disables a specific runner' do + visit runners_path(project) + + within '.available-specific-runners' do + click_on 'Enable for this project' + end - scenario 'user checks default configuration' do - visit project_runner_path(project, runner) + expect(page.find('.activated-specific-runners')).to have_content(specific_runner.display_name) - expect(page).to have_content 'Can run untagged jobs Yes' + within '.activated-specific-runners' do + click_on 'Disable for this project' + end + + expect(page.find('.available-specific-runners')).to have_content(specific_runner.display_name) + end end - context 'when runner has tags' do - before do - runner.update_attribute(:tag_list, ['tag']) + context 'when application settings have shared_runners_text' do + given(:shared_runners_text) { 'custom **shared** runners description' } + given(:shared_runners_html) { 'custom shared runners description' } + + background do + stub_application_setting(shared_runners_text: shared_runners_text) end - scenario 'user wants to prevent runner from running untagged job' do + scenario 'user sees shared runners description' do visit runners_path(project) - page.within('.activated-specific-runners') do - first('small > a').click - end - uncheck 'runner_run_untagged' - click_button 'Save changes' - - expect(page).to have_content 'Can run untagged jobs No' - expect(runner.reload.run_untagged?).to eq false + expect(page.find('.shared-runners-description')).to have_content(shared_runners_html) end end end + + context 'when a project has disabled shared_runners' do + given(:project) { create(:project, shared_runners_enabled: false) } + + background do + project.add_master(user) + end + + scenario 'user enables shared runners' do + visit runners_path(project) + + click_on 'Enable shared Runners' + + expect(page.find('.shared-runners-description')).to have_content('Disable shared Runners') + end + end end diff --git a/spec/finders/admin/projects_finder_spec.rb b/spec/finders/admin/projects_finder_spec.rb index 28e36330029..4b67203a0df 100644 --- a/spec/finders/admin/projects_finder_spec.rb +++ b/spec/finders/admin/projects_finder_spec.rb @@ -118,6 +118,12 @@ describe Admin::ProjectsFinder do it { is_expected.to match_array([archived_project, shared_project, public_project, internal_project, private_project]) } end + + context 'archived=only' do + let(:params) { { archived: 'only' } } + + it { is_expected.to eq([archived_project]) } + end end context 'filter by personal' do diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index a5de586e869..0dfe6ba9c32 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -123,6 +123,12 @@ describe ProjectsFinder do it { is_expected.to match_array([public_project, internal_project, archived_project]) } end + describe 'filter by archived only' do + let(:params) { { archived: 'only' } } + + it { is_expected.to eq([archived_project]) } + end + describe 'filter by archived for backward compatibility' do let(:params) { { archived: false } } diff --git a/spec/fixtures/emails/ios_default.eml b/spec/fixtures/emails/ios_default.eml index 8d4d58feb16..fa19475104a 100644 --- a/spec/fixtures/emails/ios_default.eml +++ b/spec/fixtures/emails/ios_default.eml @@ -76,7 +76,7 @@ Content-Transfer-Encoding: 7bit <img src="https://meta-discourse.global.ssl.fastly.net/user_avatar/meta.discourse.org/techapj/45/3281.png" title="techAPJ" style="max-width:100%;" width="45" height="45"> </td> <td> - <a href="https://meta.discourse.org/users/techapj" target="_blank" style="text-decoration: none; font-weight: bold; color: #006699;; font-size:13px;font-family:'lucida grande',tahoma,verdana,arial,sans-serif;color:#3b5998;text-decoration:none;font-weight:bold">techAPJ</a><br> + <a href="https://meta.discourse.org/users/techapj" target="_blank" style="text-decoration: none; font-weight: 600; color: #006699;; font-size:13px;font-family:'lucida grande',tahoma,verdana,arial,sans-serif;color:#3b5998;text-decoration:none;font-weight:bold">techAPJ</a><br> <span style="text-align:right;color:#999999;padding-right:5px;font-family:'lucida grande',tahoma,verdana,arial,sans-serif;font-size:11px">November 28</span> </td> </tr> @@ -94,7 +94,7 @@ Content-Transfer-Encoding: 7bit <div style="color:#666;"> - <p>To respond, reply to this email or visit <a href="https://meta.discourse.org/t/testing-default-email-replies/22638/3" style="text-decoration: none; font-weight: bold; color: #006699;; color:#666;">https://meta.discourse.org/t/testing-default-email-replies/22638/3</a> in your browser.</p> + <p>To respond, reply to this email or visit <a href="https://meta.discourse.org/t/testing-default-email-replies/22638/3" style="text-decoration: none; font-weight: 600; color: #006699;; color:#666;">https://meta.discourse.org/t/testing-default-email-replies/22638/3</a> in your browser.</p> </div> <hr style="background-color: #ddd; height: 1px; border: 1px;; background-color: #ddd; height: 1px; border: 1px;"> <h4>Previous Replies</h4> @@ -106,7 +106,7 @@ Content-Transfer-Encoding: 7bit <img src="https://meta-discourse.global.ssl.fastly.net/user_avatar/meta.discourse.org/codinghorror/45/5297.png" title="codinghorror" style="max-width:100%;" width="45" height="45"> </td> <td> - <a href="https://meta.discourse.org/users/codinghorror" target="_blank" style="text-decoration: none; font-weight: bold; color: #006699;; font-size:13px;font-family:'lucida grande',tahoma,verdana,arial,sans-serif;color:#3b5998;text-decoration:none;font-weight:bold">codinghorror</a><br> + <a href="https://meta.discourse.org/users/codinghorror" target="_blank" style="text-decoration: none; font-weight: 600; color: #006699;; font-size:13px;font-family:'lucida grande',tahoma,verdana,arial,sans-serif;color:#3b5998;text-decoration:none;font-weight:bold">codinghorror</a><br> <span style="text-align:right;color:#999999;padding-right:5px;font-family:'lucida grande',tahoma,verdana,arial,sans-serif;font-size:11px">November 28</span> </td> </tr> @@ -114,7 +114,7 @@ Content-Transfer-Encoding: 7bit <td style="padding-top:5px;" colspan="2"> <p style="margin-top:0; border: 0;">We're testing the latest GitHub email processing library which we are integrating now.</p> -<p style="margin-top:0; border: 0;"><a href="https://github.com/github/email_reply_parser" target="_blank" style="text-decoration: none; font-weight: bold; color: #006699;">https://github.com/github/email_reply_parser</a></p> +<p style="margin-top:0; border: 0;"><a href="https://github.com/github/email_reply_parser" target="_blank" style="text-decoration: none; font-weight: 600; color: #006699;">https://github.com/github/email_reply_parser</a></p> <p style="margin-top:0; border: 0;">Go ahead and reply to this topic and I'll reply from various email clients for testing.</p> </td> @@ -126,10 +126,10 @@ Content-Transfer-Encoding: 7bit <hr style="background-color: #ddd; height: 1px; border: 1px;; background-color: #ddd; height: 1px; border: 1px;"> <div style="color:#666;"> -<p>To respond, reply to this email or visit <a href="https://meta.discourse.org/t/testing-default-email-replies/22638/3" style="text-decoration: none; font-weight: bold; color: #006699;; color:#666;">https://meta.discourse.org/t/testing-default-email-replies/22638/3</a> in your browser.</p> +<p>To respond, reply to this email or visit <a href="https://meta.discourse.org/t/testing-default-email-replies/22638/3" style="text-decoration: none; font-weight: 600; color: #006699;; color:#666;">https://meta.discourse.org/t/testing-default-email-replies/22638/3</a> in your browser.</p> </div> <div style="color:#666;"> -<p>To unsubscribe from these emails, visit your <a href="https://meta.discourse.org/my/preferences" style="text-decoration: none; font-weight: bold; color: #006699;; color:#666;">user preferences</a>.</p> +<p>To unsubscribe from these emails, visit your <a href="https://meta.discourse.org/my/preferences" style="text-decoration: none; font-weight: 600; color: #006699;; color:#666;">user preferences</a>.</p> </div> </div> </div></blockquote></body></html> diff --git a/spec/fixtures/emails/on_wrote.eml b/spec/fixtures/emails/on_wrote.eml index feb59bd27bb..af6a4e50a49 100644 --- a/spec/fixtures/emails/on_wrote.eml +++ b/spec/fixtures/emails/on_wrote.eml @@ -53,7 +53,7 @@ y > display: inline-block; > font-family: FontAwesome; > font-style: normal; -> font-weight: normal; +> font-weight: 400; > line-height: 1; > -webkit-font-smoothing: antialiased; > -moz-osx-font-smoothing: grayscale; @@ -227,7 +227,7 @@ ding:5px">.fa { display: inline-block; font-family: FontAwesome; font-style: normal; - font-weight: normal; + font-weight: 400; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -274,4 +274,4 @@ ight:bold;color:#006699" target=3D"_blank">user preferences</a>.</p> </div> </blockquote></div><br></div></div> ---001a11c34c389e728f0502aa26a0--
\ No newline at end of file +--001a11c34c389e728f0502aa26a0-- diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb index d16fcf21e45..4632c679972 100644 --- a/spec/helpers/avatars_helper_spec.rb +++ b/spec/helpers/avatars_helper_spec.rb @@ -28,7 +28,7 @@ describe AvatarsHelper do it 'displays user avatar' do is_expected.to eq image_tag( LazyImageTagHelper.placeholder_image, - class: 'avatar has-tooltip s16 lazy', + class: 'avatar s16 has-tooltip lazy', alt: "#{user.name}'s avatar", title: user.name, data: { container: 'body', src: avatar_icon(user, 16) } @@ -41,7 +41,7 @@ describe AvatarsHelper do it 'uses provided css_class' do is_expected.to eq image_tag( LazyImageTagHelper.placeholder_image, - class: "avatar has-tooltip s16 #{options[:css_class]} lazy", + class: "avatar s16 #{options[:css_class]} has-tooltip lazy", alt: "#{user.name}'s avatar", title: user.name, data: { container: 'body', src: avatar_icon(user, 16) } @@ -55,7 +55,7 @@ describe AvatarsHelper do it 'uses provided size' do is_expected.to eq image_tag( LazyImageTagHelper.placeholder_image, - class: "avatar has-tooltip s#{options[:size]} lazy", + class: "avatar s#{options[:size]} has-tooltip lazy", alt: "#{user.name}'s avatar", title: user.name, data: { container: 'body', src: avatar_icon(user, options[:size]) } @@ -69,7 +69,7 @@ describe AvatarsHelper do it 'uses provided url' do is_expected.to eq image_tag( LazyImageTagHelper.placeholder_image, - class: 'avatar has-tooltip s16 lazy', + class: 'avatar s16 has-tooltip lazy', alt: "#{user.name}'s avatar", title: user.name, data: { container: 'body', src: options[:url] } @@ -77,6 +77,36 @@ describe AvatarsHelper do end end + context 'with has_tooltip parameter' do + context 'with has_tooltip set to true' do + let(:options) { { user: user, has_tooltip: true } } + + it 'adds has-tooltip' do + is_expected.to eq image_tag( + LazyImageTagHelper.placeholder_image, + class: 'avatar s16 has-tooltip lazy', + alt: "#{user.name}'s avatar", + title: user.name, + data: { container: 'body', src: avatar_icon(user, 16) } + ) + end + end + + context 'with has_tooltip set to false' do + let(:options) { { user: user, has_tooltip: false } } + + it 'does not add has-tooltip or data container' do + is_expected.to eq image_tag( + LazyImageTagHelper.placeholder_image, + class: 'avatar s16 lazy', + alt: "#{user.name}'s avatar", + title: user.name, + data: { src: avatar_icon(user, 16) } + ) + end + end + end + context 'with user_name parameter' do let(:options) { { user_name: 'Tinky Winky', user_email: 'no@f.un' } } @@ -86,7 +116,7 @@ describe AvatarsHelper do it 'prefers user parameter' do is_expected.to eq image_tag( LazyImageTagHelper.placeholder_image, - class: 'avatar has-tooltip s16 lazy', + class: 'avatar s16 has-tooltip lazy', alt: "#{user.name}'s avatar", title: user.name, data: { container: 'body', src: avatar_icon(user, 16) } @@ -97,7 +127,7 @@ describe AvatarsHelper do it 'uses user_name and user_email parameter if user is not present' do is_expected.to eq image_tag( LazyImageTagHelper.placeholder_image, - class: 'avatar has-tooltip s16 lazy', + class: 'avatar s16 has-tooltip lazy', alt: "#{options[:user_name]}'s avatar", title: options[:user_name], data: { container: 'body', src: avatar_icon(options[:user_email], 16) } diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index 4b72dbb7964..d5536fcb22b 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -106,5 +106,9 @@ describe EventsHelper do it "handles empty strings" do expect(helper.event_commit_title("")).to eq("") end + + it 'handles nil values' do + expect(helper.event_commit_title(nil)).to eq('') + end end end diff --git a/spec/javascripts/repo/components/repo_commit_section_spec.js b/spec/javascripts/repo/components/repo_commit_section_spec.js index 249a2f36fcd..e604dcc152d 100644 --- a/spec/javascripts/repo/components/repo_commit_section_spec.js +++ b/spec/javascripts/repo/components/repo_commit_section_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import repoCommitSection from '~/repo/components/repo_commit_section.vue'; import RepoStore from '~/repo/stores/repo_store'; -import Api from '~/api'; +import RepoService from '~/repo/services/repo_service'; describe('RepoCommitSection', () => { const branch = 'master'; @@ -111,7 +111,7 @@ describe('RepoCommitSection', () => { expect(submitCommit.disabled).toBeFalsy(); spyOn(vm, 'makeCommit').and.callThrough(); - spyOn(Api, 'commitMultiple'); + spyOn(RepoService, 'commitFiles').and.callFake(() => Promise.resolve()); submitCommit.click(); @@ -119,10 +119,9 @@ describe('RepoCommitSection', () => { expect(vm.makeCommit).toHaveBeenCalled(); expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeTruthy(); - const args = Api.commitMultiple.calls.allArgs()[0]; - const { commit_message, actions, branch: payloadBranch } = args[1]; + const args = RepoService.commitFiles.calls.allArgs()[0]; + const { commit_message, actions, branch: payloadBranch } = args[0]; - expect(args[0]).toBe(projectId); expect(commit_message).toBe(commitMessage); expect(actions.length).toEqual(2); expect(payloadBranch).toEqual(branch); diff --git a/spec/javascripts/repo/services/repo_service_spec.js b/spec/javascripts/repo/services/repo_service_spec.js index d74e6a67b1e..6f530770525 100644 --- a/spec/javascripts/repo/services/repo_service_spec.js +++ b/spec/javascripts/repo/services/repo_service_spec.js @@ -1,5 +1,7 @@ import axios from 'axios'; import RepoService from '~/repo/services/repo_service'; +import RepoStore from '~/repo/stores/repo_store'; +import Api from '~/api'; describe('RepoService', () => { it('has default json format param', () => { @@ -118,4 +120,52 @@ describe('RepoService', () => { }).catch(done.fail); }); }); + + describe('commitFiles', () => { + it('calls commitMultiple and .then commitFlash', (done) => { + const projectId = 'projectId'; + const payload = {}; + RepoStore.projectId = projectId; + + spyOn(Api, 'commitMultiple').and.returnValue(Promise.resolve()); + spyOn(RepoService, 'commitFlash'); + + const apiPromise = RepoService.commitFiles(payload); + + expect(Api.commitMultiple).toHaveBeenCalledWith(projectId, payload); + + apiPromise.then(() => { + expect(RepoService.commitFlash).toHaveBeenCalled(); + done(); + }).catch(done.fail); + }); + }); + + describe('commitFlash', () => { + it('calls Flash with data.message', () => { + const data = { + message: 'message', + }; + spyOn(window, 'Flash'); + + RepoService.commitFlash(data); + + expect(window.Flash).toHaveBeenCalledWith(data.message); + }); + + it('calls Flash with success string if short_id and stats', () => { + const data = { + short_id: 'short_id', + stats: { + additions: '4', + deletions: '5', + }, + }; + spyOn(window, 'Flash'); + + RepoService.commitFlash(data); + + expect(window.Flash).toHaveBeenCalledWith(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice'); + }); + }); }); diff --git a/spec/lib/gitlab/bare_repository_importer_spec.rb b/spec/lib/gitlab/bare_repository_importer_spec.rb new file mode 100644 index 00000000000..892f2dafc96 --- /dev/null +++ b/spec/lib/gitlab/bare_repository_importer_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe Gitlab::BareRepositoryImporter, repository: true do + subject(:importer) { described_class.new('default', project_path) } + let(:project_path) { 'a-group/a-sub-group/a-project' } + let!(:admin) { create(:admin) } + + before do + allow(described_class).to receive(:log) + end + + describe '.execute' do + it 'creates a project for a repository in storage' do + FileUtils.mkdir_p(File.join(TestEnv.repos_path, "#{project_path}.git")) + fake_importer = double + + expect(described_class).to receive(:new).with('default', project_path) + .and_return(fake_importer) + expect(fake_importer).to receive(:create_project_if_needed) + + described_class.execute + end + + it 'skips wiki repos' do + FileUtils.mkdir_p(File.join(TestEnv.repos_path, 'the-group', 'the-project.wiki.git')) + + expect(described_class).to receive(:log).with(' * Skipping wiki repo') + expect(described_class).not_to receive(:new) + + described_class.execute + end + end + + describe '#initialize' do + context 'without admin users' do + let(:admin) { nil } + + it 'raises an error' do + expect { importer }.to raise_error(Gitlab::BareRepositoryImporter::NoAdminError) + end + end + end + + describe '#create_project_if_needed' do + it 'starts an import for a project that did not exist' do + expect(importer).to receive(:create_project) + + importer.create_project_if_needed + end + + it 'skips importing when the project already exists' do + group = create(:group, path: 'a-group') + subgroup = create(:group, path: 'a-sub-group', parent: group) + project = create(:project, path: 'a-project', namespace: subgroup) + + expect(importer).not_to receive(:create_project) + expect(importer).to receive(:log).with(" * #{project.name} (a-group/a-sub-group/a-project) exists") + + importer.create_project_if_needed + end + + it 'creates a project with the correct path in the database' do + importer.create_project_if_needed + + expect(Project.find_by_full_path(project_path)).not_to be_nil + end + end +end diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index d3d841b0668..c91895cedc3 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -15,6 +15,17 @@ describe Gitlab::Diff::File do it { expect(diff_lines.first).to be_kind_of(Gitlab::Diff::Line) } end + describe '#highlighted_diff_lines' do + it 'highlights the diff and memoises the result' do + expect(Gitlab::Diff::Highlight).to receive(:new) + .with(diff_file, repository: project.repository) + .once + .and_call_original + + diff_file.highlighted_diff_lines + end + end + describe '#mode_changed?' do it { expect(diff_file.mode_changed?).to be_falsey } end @@ -122,8 +133,20 @@ describe Gitlab::Diff::File do let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') } let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') } - it 'returns true' do - expect(diff_file.content_changed?).to be_truthy + context 'when the blobs are different' do + it 'returns true' do + expect(diff_file.content_changed?).to be_truthy + end + end + + context 'when there are no diff refs' do + before do + allow(diff_file).to receive(:diff_refs).and_return(nil) + end + + it 'returns false' do + expect(diff_file.content_changed?).to be_falsey + end end end @@ -131,8 +154,20 @@ describe Gitlab::Diff::File do let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') } let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } - it 'returns true' do - expect(diff_file.content_changed?).to be_truthy + context 'when the blobs are different' do + it 'returns true' do + expect(diff_file.content_changed?).to be_truthy + end + end + + context 'when there are no diff refs' do + before do + allow(diff_file).to receive(:diff_refs).and_return(nil) + end + + it 'returns true' do + expect(diff_file.content_changed?).to be_truthy + end end end end @@ -270,6 +305,21 @@ describe Gitlab::Diff::File do expect(diff_file.simple_viewer).to be_a(DiffViewer::ModeChanged) end end + + context 'when no other conditions apply' do + before do + allow(diff_file).to receive(:content_changed?).and_return(false) + allow(diff_file).to receive(:new_file?).and_return(false) + allow(diff_file).to receive(:deleted_file?).and_return(false) + allow(diff_file).to receive(:renamed_file?).and_return(false) + allow(diff_file).to receive(:mode_changed?).and_return(false) + allow(diff_file).to receive(:raw_text?).and_return(false) + end + + it 'returns a No Preview viewer' do + expect(diff_file.simple_viewer).to be_a(DiffViewer::NoPreview) + end + end end describe '#rich_viewer' do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 06fcdd7aa57..b84f54c87fc 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -977,6 +977,36 @@ describe Gitlab::Git::Repository, seed_helper: true do it 'returns the number of branches' do expect(repository.branch_count).to eq(10) end + + context 'with local and remote branches' do + let(:repository) do + Gitlab::Git::Repository.new('default', File.join(TEST_MUTABLE_REPO_PATH, '.git')) + end + + before do + create_remote_branch(repository, 'joe', 'remote_branch', 'master') + repository.create_branch('local_branch', 'master') + end + + after do + FileUtils.rm_rf(TEST_MUTABLE_REPO_PATH) + ensure_seeds + end + + it 'returns the count of local branches' do + expect(repository.branch_count).to eq(repository.local_branches.count) + end + + context 'with Gitaly disabled' do + before do + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) + end + + it 'returns the count of local branches' do + expect(repository.branch_count).to eq(repository.local_branches.count) + end + end + end end describe "#ls_files" do @@ -1080,6 +1110,34 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#ref_exists?' do + shared_examples 'checks the existence of refs' do + it 'returns true for an existing tag' do + expect(repository.ref_exists?('refs/heads/master')).to eq(true) + end + + it 'returns false for a non-existing tag' do + expect(repository.ref_exists?('refs/tags/THIS_TAG_DOES_NOT_EXIST')).to eq(false) + end + + it 'raises an ArgumentError for an empty string' do + expect { repository.ref_exists?('') }.to raise_error(ArgumentError) + end + + it 'raises an ArgumentError for an invalid ref' do + expect { repository.ref_exists?('INVALID') }.to raise_error(ArgumentError) + end + end + + context 'when Gitaly ref_exists feature is enabled' do + it_behaves_like 'checks the existence of refs' + end + + context 'when Gitaly ref_exists feature is disabled', skip_gitaly_mock: true do + it_behaves_like 'checks the existence of refs' + end + end + describe '#tag_exists?' do shared_examples 'checks the existence of tags' do it 'returns true for an existing tag' do diff --git a/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb b/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb index 12366151f44..c708b15853a 100644 --- a/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb +++ b/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::Storage::ForkedStorageCheck, skip_database_cleaner: true do +describe Gitlab::Git::Storage::ForkedStorageCheck, broken_storage: true, skip_database_cleaner: true do let(:existing_path) do existing_path = TestEnv.repos_path FileUtils.mkdir_p(existing_path) diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index 2eaf4222964..f32fe5d8150 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -140,4 +140,29 @@ describe Gitlab::GitalyClient::CommitService do described_class.new(repository).find_commit(revision) end end + + describe '#patch' do + let(:request) do + Gitaly::CommitPatchRequest.new( + repository: repository_message, revision: revision + ) + end + let(:response) { [double(data: "my "), double(data: "diff")] } + + subject { described_class.new(repository).patch(revision) } + + it 'sends an RPC request' do + expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_patch) + .with(request, kind_of(Hash)).and_return([]) + + subject + end + + it 'concatenates the responses data' do + allow_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_patch) + .with(request, kind_of(Hash)).and_return(response) + + expect(subject).to eq("my diff") + end + end end diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb index d17d440d833..d9ddb4326be 100644 --- a/spec/lib/gitlab/ldap/adapter_spec.rb +++ b/spec/lib/gitlab/ldap/adapter_spec.rb @@ -16,7 +16,7 @@ describe Gitlab::LDAP::Adapter do expect(adapter).to receive(:ldap_search) do |arg| expect(arg[:filter].to_s).to eq('(uid=johndoe)') expect(arg[:base]).to eq('dc=example,dc=com') - expect(arg[:attributes]).to match(%w{uid cn dn uid userid sAMAccountName mail email userPrincipalName}) + expect(arg[:attributes]).to match(%w{dn uid cn mail email userPrincipalName}) end.and_return({}) adapter.users('uid', 'johndoe') @@ -26,7 +26,7 @@ describe Gitlab::LDAP::Adapter do expect(adapter).to receive(:ldap_search).with( base: 'uid=johndoe,ou=users,dc=example,dc=com', scope: Net::LDAP::SearchScope_BaseObject, - attributes: %w{uid cn dn uid userid sAMAccountName mail email userPrincipalName}, + attributes: %w{dn uid cn mail email userPrincipalName}, filter: nil ).and_return({}) @@ -63,7 +63,7 @@ describe Gitlab::LDAP::Adapter do it 'uses the right uid attribute when non-default' do stub_ldap_config(uid: 'sAMAccountName') expect(adapter).to receive(:ldap_search).with( - hash_including(attributes: %w{sAMAccountName cn dn uid userid sAMAccountName mail email userPrincipalName}) + hash_including(attributes: %w{dn sAMAccountName cn mail email userPrincipalName}) ).and_return({}) adapter.users('sAMAccountName', 'johndoe') diff --git a/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb b/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb new file mode 100644 index 00000000000..7879105a334 --- /dev/null +++ b/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20170816102555_cleanup_nonexisting_namespace_pending_delete_projects.rb') + +describe CleanupNonexistingNamespacePendingDeleteProjects do + before do + # Stub after_save callbacks that will fail when Project has invalid namespace + allow_any_instance_of(Project).to receive(:ensure_storage_path_exist).and_return(nil) + allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil) + end + + describe '#up' do + set(:some_project) { create(:project) } + + it 'only cleans up when namespace does not exist' do + create(:project, pending_delete: true) + project = build(:project, pending_delete: true, namespace: nil, namespace_id: Namespace.maximum(:id).to_i.succ) + project.save(validate: false) + + expect(NamespacelessProjectDestroyWorker).to receive(:bulk_perform_async).with([[project.id]]) + + described_class.new.up + end + + it 'does nothing when no pending delete projects without namespace found' do + create(:project, pending_delete: true, namespace: create(:namespace)) + + expect(NamespacelessProjectDestroyWorker).not_to receive(:bulk_perform_async) + + described_class.new.up + end + end +end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 767f0ad9e65..4f77f0d85cd 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -21,6 +21,16 @@ describe Ci::Build do it { is_expected.to respond_to(:has_trace?) } it { is_expected.to respond_to(:trace) } + describe 'callbacks' do + context 'when running after_create callback' do + it 'triggers asynchronous build hooks worker' do + expect(BuildHooksWorker).to receive(:perform_async) + + create(:ci_build) + end + end + end + describe '.manual_actions' do let!(:manual_but_created) { create(:ci_build, :manual, status: :created, pipeline: pipeline) } let!(:manual_but_succeeded) { create(:ci_build, :manual, status: :success, pipeline: pipeline) } diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index ac75c6501ee..b84e3ff18e8 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Ci::Pipeline, :mailer do let(:user) { create(:user) } - let(:project) { create(:project) } + set(:project) { create(:project) } let(:pipeline) do create(:ci_empty_pipeline, status: :created, project: project) @@ -159,6 +159,18 @@ describe Ci::Pipeline, :mailer do end end + describe '#predefined_variables' do + subject { pipeline.predefined_variables } + + it { is_expected.to be_an(Array) } + + it 'includes the defined keys' do + keys = subject.map { |v| v[:key] } + + expect(keys).to include('CI_PIPELINE_ID', 'CI_CONFIG_PATH', 'CI_PIPELINE_SOURCE') + end + end + describe '#auto_canceled?' do subject { pipeline.auto_canceled? } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index ff3224dd298..f55c161c821 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -304,6 +304,50 @@ describe Event do end end + describe '#body?' do + let(:push_event) do + event = build(:push_event) + + allow(event).to receive(:push?).and_return(true) + + event + end + + it 'returns true for a push event with commits' do + allow(push_event).to receive(:push_with_commits?).and_return(true) + + expect(push_event).to be_body + end + + it 'returns false for a push event without a valid commit range' do + allow(push_event).to receive(:push_with_commits?).and_return(false) + + expect(push_event).not_to be_body + end + + it 'returns true for a Note event' do + event = build(:event) + + allow(event).to receive(:note?).and_return(true) + + expect(event).to be_body + end + + it 'returns true if the target responds to #title' do + event = build(:event) + + allow(event).to receive(:target).and_return(double(:target, title: 'foo')) + + expect(event).to be_body + end + + it 'returns false for a regular event without a target' do + event = build(:event) + + expect(event).not_to be_body + end + end + def create_push_event(project, user) event = create(:push_event, project: project, author: user) diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 9203f6562f2..de86788d142 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -751,4 +751,22 @@ describe Issue do end end end + + describe 'removing an issue' do + it 'refreshes the number of open issues of the project' do + project = subject.project + + expect { subject.destroy } + .to change { project.open_issues_count }.from(1).to(0) + end + end + + describe '.public_only' do + it 'only returns public issues' do + public_issue = create(:issue) + create(:issue, confidential: true) + + expect(described_class.public_only).to eq([public_issue]) + end + end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 026bdbd26d1..2d10c6ef1da 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -604,7 +604,7 @@ describe MergeRequest do request = build_stubbed(:merge_request) expect(request.merge_commit_message) - .to match("See merge request #{request.to_reference}") + .to match("See merge request #{request.to_reference(full: true)}") end it 'excludes multiple linebreak runs when description is blank' do @@ -1692,4 +1692,13 @@ describe MergeRequest do expect(subject.ref_fetched?).to be_falsey end end + + describe 'removing a merge request' do + it 'refreshes the number of open merge requests of the target project' do + project = subject.target_project + + expect { subject.destroy } + .to change { project.open_merge_requests_count }.from(1).to(0) + end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 9a9e255f874..8e04eea56a7 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1356,7 +1356,7 @@ describe User do end it "excludes push event if branch has been deleted" do - allow_any_instance_of(Repository).to receive(:branch_names).and_return(['foo']) + allow_any_instance_of(Repository).to receive(:branch_exists?).with('master').and_return(false) expect(subject.recent_push).to eq(nil) end diff --git a/spec/services/groups/nested_create_service_spec.rb b/spec/services/groups/nested_create_service_spec.rb new file mode 100644 index 00000000000..c1526456bac --- /dev/null +++ b/spec/services/groups/nested_create_service_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe Groups::NestedCreateService do + let(:user) { create(:user) } + let(:params) { { group_path: 'a-group/a-sub-group' } } + + subject(:service) { described_class.new(user, params) } + + describe "#execute" do + it 'returns the group if it already existed' do + parent = create(:group, path: 'a-group', owner: user) + child = create(:group, path: 'a-sub-group', parent: parent, owner: user) + + expect(service.execute).to eq(child) + end + + it 'reuses a parent if it already existed' do + parent = create(:group, path: 'a-group') + parent.add_owner(user) + + expect(service.execute.parent).to eq(parent) + end + + it 'creates group and subgroup in the database' do + service.execute + + parent = Group.find_by_full_path('a-group') + child = parent.children.find_by(path: 'a-sub-group') + + expect(parent).not_to be_nil + expect(child).not_to be_nil + end + + it 'creates the group with correct visibility level' do + allow(Gitlab::CurrentSettings.current_application_settings) + .to receive(:default_group_visibility) { Gitlab::VisibilityLevel::INTERNAL } + + group = service.execute + + expect(group.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL) + end + + context 'adding a visibility level ' do + let(:params) { { group_path: 'a-group/a-sub-group', visibility_level: Gitlab::VisibilityLevel::PRIVATE } } + + it 'overwrites the visibility level' do + group = service.execute + + expect(group.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + end + end + end +end diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index a03f68434de..171f70c32a8 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -42,6 +42,11 @@ describe Issues::CloseService do service.execute(issue) end + it 'refreshes the number of open issues' do + expect { service.execute(issue) } + .to change { project.open_issues_count }.from(1).to(0) + end + it 'invalidates counter cache for assignees' do expect_any_instance_of(User).to receive(:invalidate_issue_cache_counts) diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 9b2d9e79f4f..78b11cd7991 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -35,6 +35,10 @@ describe Issues::CreateService do expect(issue.due_date).to eq Date.tomorrow end + it 'refreshes the number of open issues' do + expect { issue }.to change { project.open_issues_count }.from(0).to(1) + end + context 'when current user cannot admin issues in the project' do let(:guest) { create(:user) } diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb index 205e9ebd237..48fc98b3b2f 100644 --- a/spec/services/issues/reopen_service_spec.rb +++ b/spec/services/issues/reopen_service_spec.rb @@ -34,6 +34,13 @@ describe Issues::ReopenService do described_class.new(project, user).execute(issue) end + it 'refreshes the number of opened issues' do + service = described_class.new(project, user) + + expect { service.execute(issue) } + .to change { project.open_issues_count }.from(0).to(1) + end + context 'when issue is not confidential' do it 'executes issue hooks' do expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks) diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb index 04bf267d167..7e65369762c 100644 --- a/spec/services/merge_requests/close_service_spec.rb +++ b/spec/services/merge_requests/close_service_spec.rb @@ -52,6 +52,13 @@ describe MergeRequests::CloseService do end end + it 'refreshes the number of open merge requests for a valid MR' do + service = described_class.new(project, user, {}) + + expect { service.execute(merge_request) } + .to change { project.open_merge_requests_count }.from(1).to(0) + end + context 'current user is not authorized to close merge request' do before do perform_enqueued_jobs do diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index a1f3bec42cc..d6409c0d625 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -18,31 +18,35 @@ describe MergeRequests::CreateService do end let(:service) { described_class.new(project, user, opts) } + let(:merge_request) { service.execute } before do project.team << [user, :master] project.team << [assignee, :developer] allow(service).to receive(:execute_hooks) - - @merge_request = service.execute end it 'creates an MR' do - expect(@merge_request).to be_valid - expect(@merge_request.title).to eq('Awesome merge_request') - expect(@merge_request.assignee).to be_nil - expect(@merge_request.merge_params['force_remove_source_branch']).to eq('1') + expect(merge_request).to be_valid + expect(merge_request.title).to eq('Awesome merge_request') + expect(merge_request.assignee).to be_nil + expect(merge_request.merge_params['force_remove_source_branch']).to eq('1') end it 'executes hooks with default action' do - expect(service).to have_received(:execute_hooks).with(@merge_request) + expect(service).to have_received(:execute_hooks).with(merge_request) + end + + it 'refreshes the number of open merge requests' do + expect { service.execute } + .to change { project.open_merge_requests_count }.from(0).to(1) end it 'does not creates todos' do attributes = { project: project, - target_id: @merge_request.id, - target_type: @merge_request.class.name + target_id: merge_request.id, + target_type: merge_request.class.name } expect(Todo.where(attributes).count).to be_zero @@ -51,8 +55,8 @@ describe MergeRequests::CreateService do it 'creates exactly 1 create MR event' do attributes = { action: Event::CREATED, - target_id: @merge_request.id, - target_type: @merge_request.class.name + target_id: merge_request.id, + target_type: merge_request.class.name } expect(Event.where(attributes).count).to eq(1) @@ -69,15 +73,15 @@ describe MergeRequests::CreateService do } end - it { expect(@merge_request.assignee).to eq assignee } + it { expect(merge_request.assignee).to eq assignee } it 'creates a todo for new assignee' do attributes = { project: project, author: user, user: assignee, - target_id: @merge_request.id, - target_type: @merge_request.class.name, + target_id: merge_request.id, + target_type: merge_request.class.name, action: Todo::ASSIGNED, state: :pending } diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb index f02af0c582e..fa652611c6b 100644 --- a/spec/services/merge_requests/reopen_service_spec.rb +++ b/spec/services/merge_requests/reopen_service_spec.rb @@ -47,6 +47,13 @@ describe MergeRequests::ReopenService do end end + it 'refreshes the number of open merge requests for a valid MR' do + service = described_class.new(project, user, {}) + + expect { service.execute(merge_request) } + .to change { project.open_merge_requests_count }.from(0).to(1) + end + context 'current user is not authorized to reopen merge request' do before do perform_enqueued_jobs do diff --git a/spec/services/projects/count_service_spec.rb b/spec/services/projects/count_service_spec.rb new file mode 100644 index 00000000000..79b01e7620e --- /dev/null +++ b/spec/services/projects/count_service_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe Projects::CountService do + let(:project) { build(:project, id: 1) } + let(:service) { described_class.new(project) } + + describe '#relation_for_count' do + it 'raises NotImplementedError' do + expect { service.relation_for_count }.to raise_error(NotImplementedError) + end + end + + describe '#count' do + before do + allow(service).to receive(:cache_key_name).and_return('count_service') + end + + it 'returns the number of rows' do + allow(service).to receive(:uncached_count).and_return(1) + + expect(service.count).to eq(1) + end + + it 'caches the number of rows', :use_clean_rails_memory_store_caching do + expect(service).to receive(:uncached_count).once.and_return(1) + + 2.times do + expect(service.count).to eq(1) + end + end + end + + describe '#refresh_cache', :use_clean_rails_memory_store_caching do + before do + allow(service).to receive(:cache_key_name).and_return('count_service') + end + + it 'refreshes the cache' do + expect(service).to receive(:uncached_count).once.and_return(1) + + service.refresh_cache + + expect(service.count).to eq(1) + end + end + + describe '#delete_cache', :use_clean_rails_memory_store_caching do + before do + allow(service).to receive(:cache_key_name).and_return('count_service') + end + + it 'removes the cache' do + expect(service).to receive(:uncached_count).twice.and_return(1) + + service.count + service.delete_cache + service.count + end + end + + describe '#cache_key_name' do + it 'raises NotImplementedError' do + expect { service.cache_key_name }.to raise_error(NotImplementedError) + end + end + + describe '#cache_key' do + it 'returns the cache key as an Array' do + allow(service).to receive(:cache_key_name).and_return('count_service') + expect(service.cache_key).to eq(['projects', 1, 'count_service']) + end + end +end diff --git a/spec/services/projects/forks_count_service_spec.rb b/spec/services/projects/forks_count_service_spec.rb index cf299c5d09b..9f8e7ee18a8 100644 --- a/spec/services/projects/forks_count_service_spec.rb +++ b/spec/services/projects/forks_count_service_spec.rb @@ -1,40 +1,14 @@ require 'spec_helper' describe Projects::ForksCountService do - let(:project) { build(:project, id: 42) } - let(:service) { described_class.new(project) } - describe '#count' do it 'returns the number of forks' do - allow(service).to receive(:uncached_count).and_return(1) - - expect(service.count).to eq(1) - end - - it 'caches the forks count', :use_clean_rails_memory_store_caching do - expect(service).to receive(:uncached_count).once.and_return(1) + project = build(:project, id: 42) + service = described_class.new(project) - 2.times { service.count } - end - end - - describe '#refresh_cache', :use_clean_rails_memory_store_caching do - it 'refreshes the cache' do - expect(service).to receive(:uncached_count).once.and_return(1) - - service.refresh_cache + allow(service).to receive(:uncached_count).and_return(1) expect(service.count).to eq(1) end end - - describe '#delete_cache', :use_clean_rails_memory_store_caching do - it 'removes the cache' do - expect(service).to receive(:uncached_count).twice.and_return(1) - - service.count - service.delete_cache - service.count - end - end end diff --git a/spec/services/projects/open_issues_count_service_spec.rb b/spec/services/projects/open_issues_count_service_spec.rb new file mode 100644 index 00000000000..f964f9972cd --- /dev/null +++ b/spec/services/projects/open_issues_count_service_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Projects::OpenIssuesCountService do + describe '#count' do + it 'returns the number of open issues' do + project = create(:project) + create(:issue, :opened, project: project) + + expect(described_class.new(project).count).to eq(1) + end + + it 'does not include confidential issues in the issue count' do + project = create(:project) + + create(:issue, :opened, project: project) + create(:issue, :opened, confidential: true, project: project) + + expect(described_class.new(project).count).to eq(1) + end + end +end diff --git a/spec/services/projects/open_merge_requests_count_service_spec.rb b/spec/services/projects/open_merge_requests_count_service_spec.rb new file mode 100644 index 00000000000..9f49b9ec6a2 --- /dev/null +++ b/spec/services/projects/open_merge_requests_count_service_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe Projects::OpenMergeRequestsCountService do + describe '#count' do + it 'returns the number of open merge requests' do + project = create(:project) + create(:merge_request, + :opened, + source_project: project, + target_project: project) + + expect(described_class.new(project).count).to eq(1) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c10197ff651..ff1754fbe7e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -105,6 +105,18 @@ RSpec.configure do |config| reset_delivered_emails! end + # Stub the `ForkedStorageCheck.storage_available?` method unless + # `:broken_storage` metadata is defined + # + # This check can be slow and is unnecessary in a test environment where we + # know the storage is available, because we create it at runtime + config.before(:example) do |example| + unless example.metadata[:broken_storage] + allow(Gitlab::Git::Storage::ForkedStorageCheck) + .to receive(:storage_available?).and_return(true) + end + end + config.around(:each, :use_clean_rails_memory_store_caching) do |example| caching_store = Rails.cache Rails.cache = ActiveSupport::Cache::MemoryStore.new diff --git a/spec/support/cycle_analytics_helpers.rb b/spec/support/cycle_analytics_helpers.rb index 30911e7fa86..39586d37e93 100644 --- a/spec/support/cycle_analytics_helpers.rb +++ b/spec/support/cycle_analytics_helpers.rb @@ -78,6 +78,8 @@ module CycleAnalyticsHelpers @dummy_pipeline ||= Ci::Pipeline.new( sha: project.repository.commit('master').sha, + ref: 'master', + source: :push, project: project) end diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index b0f520d08e8..edaee03ea6c 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -4,18 +4,18 @@ RSpec.configure do |config| end config.append_after(:context) do - DatabaseCleaner.clean_with(:truncation) + DatabaseCleaner.clean_with(:truncation, cache_tables: false) end config.before(:each) do DatabaseCleaner.strategy = :transaction end - config.before(:each, js: true) do + config.before(:each, :js) do DatabaseCleaner.strategy = :truncation end - config.before(:each, truncate: true) do + config.before(:each, :truncate) do DatabaseCleaner.strategy = :truncation end diff --git a/spec/workers/build_finished_worker_spec.rb b/spec/workers/build_finished_worker_spec.rb index 2868167c7d4..8cc3f37ebe8 100644 --- a/spec/workers/build_finished_worker_spec.rb +++ b/spec/workers/build_finished_worker_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe BuildFinishedWorker do describe '#perform' do context 'when build exists' do - let(:build) { create(:ci_build) } + let!(:build) { create(:ci_build) } it 'calculates coverage and calls hooks' do expect(BuildCoverageWorker) diff --git a/spec/workers/namespaceless_project_destroy_worker_spec.rb b/spec/workers/namespaceless_project_destroy_worker_spec.rb index 817e103fd9a..20cf580af8a 100644 --- a/spec/workers/namespaceless_project_destroy_worker_spec.rb +++ b/spec/workers/namespaceless_project_destroy_worker_spec.rb @@ -75,5 +75,19 @@ describe NamespacelessProjectDestroyWorker do end end end + + context 'project has non-existing namespace' do + let!(:project) do + project = build(:project, namespace_id: Namespace.maximum(:id).to_i.succ) + project.save(validate: false) + project + end + + it 'deletes the project' do + subject.perform(project.id) + + expect(Project.unscoped.all).not_to include(project) + end + end end end diff --git a/vendor/assets/stylesheets/katex.scss b/vendor/assets/stylesheets/katex.scss index 9dd8a30bf51..b45836716f2 100644 --- a/vendor/assets/stylesheets/katex.scss +++ b/vendor/assets/stylesheets/katex.scss @@ -45,112 +45,112 @@ SOFTWARE. font-family: 'KaTeX_AMS'; src: url(font-path('KaTeX_AMS-Regular.eot')); src: url(font-path('KaTeX_AMS-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_AMS-Regular.woff2')) format('woff2'), url(font-path('KaTeX_AMS-Regular.woff')) format('woff'), url(font-path('KaTeX_AMS-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Caligraphic'; src: url(font-path('KaTeX_Caligraphic-Bold.eot')); src: url(font-path('KaTeX_Caligraphic-Bold.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Caligraphic-Bold.woff2')) format('woff2'), url(font-path('KaTeX_Caligraphic-Bold.woff')) format('woff'), url(font-path('KaTeX_Caligraphic-Bold.ttf')) format('truetype'); - font-weight: bold; + font-weight: 600; font-style: normal; } @font-face { font-family: 'KaTeX_Caligraphic'; src: url(font-path('KaTeX_Caligraphic-Regular.eot')); src: url(font-path('KaTeX_Caligraphic-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Caligraphic-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Caligraphic-Regular.woff')) format('woff'), url(font-path('KaTeX_Caligraphic-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Fraktur'; src: url(font-path('KaTeX_Fraktur-Bold.eot')); src: url(font-path('KaTeX_Fraktur-Bold.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Fraktur-Bold.woff2')) format('woff2'), url(font-path('KaTeX_Fraktur-Bold.woff')) format('woff'), url(font-path('KaTeX_Fraktur-Bold.ttf')) format('truetype'); - font-weight: bold; + font-weight: 600; font-style: normal; } @font-face { font-family: 'KaTeX_Fraktur'; src: url(font-path('KaTeX_Fraktur-Regular.eot')); src: url(font-path('KaTeX_Fraktur-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Fraktur-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Fraktur-Regular.woff')) format('woff'), url(font-path('KaTeX_Fraktur-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Main'; src: url(font-path('KaTeX_Main-Bold.eot')); src: url(font-path('KaTeX_Main-Bold.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Main-Bold.woff2')) format('woff2'), url(font-path('KaTeX_Main-Bold.woff')) format('woff'), url(font-path('KaTeX_Main-Bold.ttf')) format('truetype'); - font-weight: bold; + font-weight: 600; font-style: normal; } @font-face { font-family: 'KaTeX_Main'; src: url(font-path('KaTeX_Main-Italic.eot')); src: url(font-path('KaTeX_Main-Italic.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Main-Italic.woff2')) format('woff2'), url(font-path('KaTeX_Main-Italic.woff')) format('woff'), url(font-path('KaTeX_Main-Italic.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: italic; } @font-face { font-family: 'KaTeX_Main'; src: url(font-path('KaTeX_Main-Regular.eot')); src: url(font-path('KaTeX_Main-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Main-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Main-Regular.woff')) format('woff'), url(font-path('KaTeX_Main-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Math'; src: url(font-path('KaTeX_Math-Italic.eot')); src: url(font-path('KaTeX_Math-Italic.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Math-Italic.woff2')) format('woff2'), url(font-path('KaTeX_Math-Italic.woff')) format('woff'), url(font-path('KaTeX_Math-Italic.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: italic; } @font-face { font-family: 'KaTeX_SansSerif'; src: url(font-path('KaTeX_SansSerif-Regular.eot')); src: url(font-path('KaTeX_SansSerif-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_SansSerif-Regular.woff2')) format('woff2'), url(font-path('KaTeX_SansSerif-Regular.woff')) format('woff'), url(font-path('KaTeX_SansSerif-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Script'; src: url(font-path('KaTeX_Script-Regular.eot')); src: url(font-path('KaTeX_Script-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Script-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Script-Regular.woff')) format('woff'), url(font-path('KaTeX_Script-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Size1'; src: url(font-path('KaTeX_Size1-Regular.eot')); src: url(font-path('KaTeX_Size1-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size1-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size1-Regular.woff')) format('woff'), url(font-path('KaTeX_Size1-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Size2'; src: url(font-path('KaTeX_Size2-Regular.eot')); src: url(font-path('KaTeX_Size2-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size2-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size2-Regular.woff')) format('woff'), url(font-path('KaTeX_Size2-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Size3'; src: url(font-path('KaTeX_Size3-Regular.eot')); src: url(font-path('KaTeX_Size3-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size3-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size3-Regular.woff')) format('woff'), url(font-path('KaTeX_Size3-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Size4'; src: url(font-path('KaTeX_Size4-Regular.eot')); src: url(font-path('KaTeX_Size4-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size4-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size4-Regular.woff')) format('woff'), url(font-path('KaTeX_Size4-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } @font-face { font-family: 'KaTeX_Typewriter'; src: url(font-path('KaTeX_Typewriter-Regular.eot')); src: url(font-path('KaTeX_Typewriter-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Typewriter-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Typewriter-Regular.woff')) format('woff'), url(font-path('KaTeX_Typewriter-Regular.ttf')) format('truetype'); - font-weight: normal; + font-weight: 400; font-style: normal; } .katex-display { @@ -192,7 +192,7 @@ SOFTWARE. } .katex .mathbf { font-family: KaTeX_Main; - font-weight: bold; + font-weight: 600; } .katex .amsrm { font-family: KaTeX_AMS; diff --git a/vendor/assets/stylesheets/xterm/xterm.css b/vendor/assets/stylesheets/xterm/xterm.css index b30d7b493f1..fabc51b0e3d 100644 --- a/vendor/assets/stylesheets/xterm/xterm.css +++ b/vendor/assets/stylesheets/xterm/xterm.css @@ -142,7 +142,7 @@ * Determine default colors for xterm.js */ .terminal .xterm-bold { - font-weight: bold; + font-weight: 600; } .terminal .xterm-underline { |