Commit ab6c8935 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'master' into new-nav-fix-contextual-breadcrumbs

parents 8e9ae780 2b827d05
...@@ -3,3 +3,4 @@ lib/gitlab/sanitizers/svg/whitelist.rb ...@@ -3,3 +3,4 @@ lib/gitlab/sanitizers/svg/whitelist.rb
lib/gitlab/diff/position_tracer.rb lib/gitlab/diff/position_tracer.rb
app/policies/project_policy.rb app/policies/project_policy.rb
app/models/concerns/relative_positioning.rb app/models/concerns/relative_positioning.rb
lib/gitlab/redis/*.rb
...@@ -31,6 +31,9 @@ eslint-report.html ...@@ -31,6 +31,9 @@ eslint-report.html
/config/initializers/smtp_settings.rb /config/initializers/smtp_settings.rb
/config/initializers/relative_url.rb /config/initializers/relative_url.rb
/config/resque.yml /config/resque.yml
/config/redis.cache.yml
/config/redis.queues.yml
/config/redis.shared_state.yml
/config/unicorn.rb /config/unicorn.rb
/config/secrets.yml /config/secrets.yml
/config/sidekiq.yml /config/sidekiq.yml
......
...@@ -203,69 +203,69 @@ setup-test-env: ...@@ -203,69 +203,69 @@ setup-test-env:
- public/assets - public/assets
- tmp/tests - tmp/tests
rspec-pg 0 20: *rspec-knapsack-pg rspec-pg 0 25: *rspec-knapsack-pg
rspec-pg 1 20: *rspec-knapsack-pg rspec-pg 1 25: *rspec-knapsack-pg
rspec-pg 2 20: *rspec-knapsack-pg rspec-pg 2 25: *rspec-knapsack-pg
rspec-pg 3 20: *rspec-knapsack-pg rspec-pg 3 25: *rspec-knapsack-pg
rspec-pg 4 20: *rspec-knapsack-pg rspec-pg 4 25: *rspec-knapsack-pg
rspec-pg 5 20: *rspec-knapsack-pg rspec-pg 5 25: *rspec-knapsack-pg
rspec-pg 6 20: *rspec-knapsack-pg rspec-pg 6 25: *rspec-knapsack-pg
rspec-pg 7 20: *rspec-knapsack-pg rspec-pg 7 25: *rspec-knapsack-pg
rspec-pg 8 20: *rspec-knapsack-pg rspec-pg 8 25: *rspec-knapsack-pg
rspec-pg 9 20: *rspec-knapsack-pg rspec-pg 9 25: *rspec-knapsack-pg
rspec-pg 10 20: *rspec-knapsack-pg rspec-pg 10 25: *rspec-knapsack-pg
rspec-pg 11 20: *rspec-knapsack-pg rspec-pg 11 25: *rspec-knapsack-pg
rspec-pg 12 20: *rspec-knapsack-pg rspec-pg 12 25: *rspec-knapsack-pg
rspec-pg 13 20: *rspec-knapsack-pg rspec-pg 13 25: *rspec-knapsack-pg
rspec-pg 14 20: *rspec-knapsack-pg rspec-pg 14 25: *rspec-knapsack-pg
rspec-pg 15 20: *rspec-knapsack-pg rspec-pg 15 25: *rspec-knapsack-pg
rspec-pg 16 20: *rspec-knapsack-pg rspec-pg 16 25: *rspec-knapsack-pg
rspec-pg 17 20: *rspec-knapsack-pg rspec-pg 17 25: *rspec-knapsack-pg
rspec-pg 18 20: *rspec-knapsack-pg rspec-pg 18 25: *rspec-knapsack-pg
rspec-pg 19 20: *rspec-knapsack-pg rspec-pg 19 25: *rspec-knapsack-pg
rspec-pg 20 25: *rspec-knapsack-pg
rspec-mysql 0 20: *rspec-knapsack-mysql rspec-pg 21 25: *rspec-knapsack-pg
rspec-mysql 1 20: *rspec-knapsack-mysql rspec-pg 22 25: *rspec-knapsack-pg
rspec-mysql 2 20: *rspec-knapsack-mysql rspec-pg 23 25: *rspec-knapsack-pg
rspec-mysql 3 20: *rspec-knapsack-mysql rspec-pg 24 25: *rspec-knapsack-pg
rspec-mysql 4 20: *rspec-knapsack-mysql
rspec-mysql 5 20: *rspec-knapsack-mysql rspec-mysql 0 25: *rspec-knapsack-mysql
rspec-mysql 6 20: *rspec-knapsack-mysql rspec-mysql 1 25: *rspec-knapsack-mysql
rspec-mysql 7 20: *rspec-knapsack-mysql rspec-mysql 2 25: *rspec-knapsack-mysql
rspec-mysql 8 20: *rspec-knapsack-mysql rspec-mysql 3 25: *rspec-knapsack-mysql
rspec-mysql 9 20: *rspec-knapsack-mysql rspec-mysql 4 25: *rspec-knapsack-mysql
rspec-mysql 10 20: *rspec-knapsack-mysql rspec-mysql 5 25: *rspec-knapsack-mysql
rspec-mysql 11 20: *rspec-knapsack-mysql rspec-mysql 6 25: *rspec-knapsack-mysql
rspec-mysql 12 20: *rspec-knapsack-mysql rspec-mysql 7 25: *rspec-knapsack-mysql
rspec-mysql 13 20: *rspec-knapsack-mysql rspec-mysql 8 25: *rspec-knapsack-mysql
rspec-mysql 14 20: *rspec-knapsack-mysql rspec-mysql 9 25: *rspec-knapsack-mysql
rspec-mysql 15 20: *rspec-knapsack-mysql rspec-mysql 10 25: *rspec-knapsack-mysql
rspec-mysql 16 20: *rspec-knapsack-mysql rspec-mysql 11 25: *rspec-knapsack-mysql
rspec-mysql 17 20: *rspec-knapsack-mysql rspec-mysql 12 25: *rspec-knapsack-mysql
rspec-mysql 18 20: *rspec-knapsack-mysql rspec-mysql 13 25: *rspec-knapsack-mysql
rspec-mysql 19 20: *rspec-knapsack-mysql rspec-mysql 14 25: *rspec-knapsack-mysql
rspec-mysql 15 25: *rspec-knapsack-mysql
spinach-pg 0 10: *spinach-knapsack-pg rspec-mysql 16 25: *rspec-knapsack-mysql
spinach-pg 1 10: *spinach-knapsack-pg rspec-mysql 17 25: *rspec-knapsack-mysql
spinach-pg 2 10: *spinach-knapsack-pg rspec-mysql 18 25: *rspec-knapsack-mysql
spinach-pg 3 10: *spinach-knapsack-pg rspec-mysql 19 25: *rspec-knapsack-mysql
spinach-pg 4 10: *spinach-knapsack-pg rspec-mysql 20 25: *rspec-knapsack-mysql
spinach-pg 5 10: *spinach-knapsack-pg rspec-mysql 21 25: *rspec-knapsack-mysql
spinach-pg 6 10: *spinach-knapsack-pg rspec-mysql 22 25: *rspec-knapsack-mysql
spinach-pg 7 10: *spinach-knapsack-pg rspec-mysql 23 25: *rspec-knapsack-mysql
spinach-pg 8 10: *spinach-knapsack-pg rspec-mysql 24 25: *rspec-knapsack-mysql
spinach-pg 9 10: *spinach-knapsack-pg
spinach-pg 0 5: *spinach-knapsack-pg
spinach-mysql 0 10: *spinach-knapsack-mysql spinach-pg 1 5: *spinach-knapsack-pg
spinach-mysql 1 10: *spinach-knapsack-mysql spinach-pg 2 5: *spinach-knapsack-pg
spinach-mysql 2 10: *spinach-knapsack-mysql spinach-pg 3 5: *spinach-knapsack-pg
spinach-mysql 3 10: *spinach-knapsack-mysql spinach-pg 4 5: *spinach-knapsack-pg
spinach-mysql 4 10: *spinach-knapsack-mysql
spinach-mysql 5 10: *spinach-knapsack-mysql spinach-mysql 0 5: *spinach-knapsack-mysql
spinach-mysql 6 10: *spinach-knapsack-mysql spinach-mysql 1 5: *spinach-knapsack-mysql
spinach-mysql 7 10: *spinach-knapsack-mysql spinach-mysql 2 5: *spinach-knapsack-mysql
spinach-mysql 8 10: *spinach-knapsack-mysql spinach-mysql 3 5: *spinach-knapsack-mysql
spinach-mysql 9 10: *spinach-knapsack-mysql spinach-mysql 4 5: *spinach-knapsack-mysql
# Static analysis jobs # Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis .ruby-static-analysis: &ruby-static-analysis
...@@ -557,3 +557,4 @@ gitlab_git_test: ...@@ -557,3 +557,4 @@ gitlab_git_test:
SETUP_DB: "false" SETUP_DB: "false"
script: script:
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes - spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
<<: *except-docs
...@@ -93,7 +93,7 @@ linters: ...@@ -93,7 +93,7 @@ linters:
# The basenames of @imported SCSS partials should not begin with an # The basenames of @imported SCSS partials should not begin with an
# underscore and should not include the filename extension. # underscore and should not include the filename extension.
ImportPath: ImportPath:
enabled: false enabled: true
# Avoid using !important in properties. It is usually indicative of a # Avoid using !important in properties. It is usually indicative of a
# misunderstanding of CSS specificity and can lead to brittle code. # misunderstanding of CSS specificity and can lead to brittle code.
...@@ -133,7 +133,7 @@ linters: ...@@ -133,7 +133,7 @@ linters:
# Reports when you use an unknown or disabled CSS property # Reports when you use an unknown or disabled CSS property
# (ignoring vendor-prefixed properties). # (ignoring vendor-prefixed properties).
PropertySpelling: PropertySpelling:
enabled: false enabled: true
# Configure which units are allowed for property values. # Configure which units are allowed for property values.
PropertyUnits: PropertyUnits:
...@@ -176,7 +176,7 @@ linters: ...@@ -176,7 +176,7 @@ linters:
# Commas in lists should be followed by a space. # Commas in lists should be followed by a space.
SpaceAfterComma: SpaceAfterComma:
enabled: false enabled: true
# Properties should be formatted with a single space separating the colon # Properties should be formatted with a single space separating the colon
# from the property's value. # from the property's value.
......
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 9.3.6 (2017-07-12)
- Fix API Scoping. !12300
- Username and password are no longer stripped from import url on mirror update. !12725
- Fix issues with non-UTF8 filenames by always fixing the encoding of tree and blob paths.
- Fixed GFM references not being included when updating issues inline.
## 9.3.5 (2017-07-05) ## 9.3.5 (2017-07-05)
- Remove "Remove from board" button from backlog and closed list. !12430 - Remove "Remove from board" button from backlog and closed list. !12430
......
5.1.1 5.3.0
\ No newline at end of file
...@@ -2,7 +2,6 @@ source 'https://rubygems.org' ...@@ -2,7 +2,6 @@ source 'https://rubygems.org'
gem 'rails', '4.2.8' gem 'rails', '4.2.8'
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
gem 'bootsnap', '~> 1.1'
# Responders respond_to and respond_with # Responders respond_to and respond_with
gem 'responders', '~> 2.0' gem 'responders', '~> 2.0'
...@@ -251,7 +250,6 @@ gem 'jquery-rails', '~> 4.1.0' ...@@ -251,7 +250,6 @@ gem 'jquery-rails', '~> 4.1.0'
gem 'request_store', '~> 1.3' gem 'request_store', '~> 1.3'
gem 'select2-rails', '~> 3.5.9' gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1' gem 'virtus', '~> 1.0.1'
gem 'net-ssh', '~> 3.0.1'
gem 'base32', '~> 0.3.0' gem 'base32', '~> 0.3.0'
# Sentry integration # Sentry integration
......
...@@ -83,8 +83,6 @@ GEM ...@@ -83,8 +83,6 @@ GEM
bindata (2.3.5) bindata (2.3.5)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootsnap (1.1.1)
msgpack (~> 1.0)
bootstrap-sass (3.3.6) bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4) sass (>= 3.3.4)
...@@ -465,7 +463,6 @@ GEM ...@@ -465,7 +463,6 @@ GEM
minitest (5.7.0) minitest (5.7.0)
mmap2 (2.2.7) mmap2 (2.2.7)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
msgpack (1.1.0)
multi_json (1.12.1) multi_json (1.12.1)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
...@@ -475,7 +472,6 @@ GEM ...@@ -475,7 +472,6 @@ GEM
mustermann (= 0.4.0) mustermann (= 0.4.0)
mysql2 (0.3.20) mysql2 (0.3.20)
net-ldap (0.12.1) net-ldap (0.12.1)
net-ssh (3.0.1)
netrc (0.11.0) netrc (0.11.0)
nokogiri (1.6.8.1) nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0) mini_portile2 (~> 2.1.0)
...@@ -783,7 +779,7 @@ GEM ...@@ -783,7 +779,7 @@ GEM
rack rack
shoulda-matchers (2.8.0) shoulda-matchers (2.8.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
sidekiq (5.0.0) sidekiq (5.0.4)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0) rack-protection (>= 1.5.0)
...@@ -930,7 +926,6 @@ DEPENDENCIES ...@@ -930,7 +926,6 @@ DEPENDENCIES
benchmark-ips (~> 2.3.0) benchmark-ips (~> 2.3.0)
better_errors (~> 2.1.0) better_errors (~> 2.1.0)
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
bootsnap (~> 1.1)
bootstrap-sass (~> 3.3.0) bootstrap-sass (~> 3.3.0)
bootstrap_form (~> 2.7.0) bootstrap_form (~> 2.7.0)
brakeman (~> 3.6.0) brakeman (~> 3.6.0)
...@@ -1017,7 +1012,6 @@ DEPENDENCIES ...@@ -1017,7 +1012,6 @@ DEPENDENCIES
minitest (~> 5.7.0) minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6) mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16) mysql2 (~> 0.3.16)
net-ssh (~> 3.0.1)
nokogiri (~> 1.6.7, >= 1.6.7.2) nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.4) oauth2 (~> 1.4)
octokit (~> 4.6.2) octokit (~> 4.6.2)
......
...@@ -46,6 +46,8 @@ export default { ...@@ -46,6 +46,8 @@ export default {
}, },
methods: { methods: {
changePage(e) { changePage(e) {
if (e.target.parentElement.classList.contains('disabled')) return;
const text = e.target.innerText; const text = e.target.innerText;
const { totalPages, nextPage, previousPage } = this.pageInfo; const { totalPages, nextPage, previousPage } = this.pageInfo;
...@@ -82,7 +84,9 @@ export default { ...@@ -82,7 +84,9 @@ export default {
const page = this.pageInfo.page; const page = this.pageInfo.page;
const items = []; const items = [];
if (page > 1) items.push({ title: FIRST }); if (page > 1) {
items.push({ title: FIRST, first: true });
}
if (page > 1) { if (page > 1) {
items.push({ title: PREV, prev: true }); items.push({ title: PREV, prev: true });
...@@ -110,7 +114,9 @@ export default { ...@@ -110,7 +114,9 @@ export default {
items.push({ title: NEXT, next: true }); items.push({ title: NEXT, next: true });
} }
if (total - page >= 1) items.push({ title: LAST, last: true }); if (total - page >= 1) {
items.push({ title: LAST, last: true });
}
return items; return items;
}, },
...@@ -124,13 +130,15 @@ export default { ...@@ -124,13 +130,15 @@ export default {
v-for="item in getItems" v-for="item in getItems"
:class="{ :class="{
page: item.page, page: item.page,
prev: item.prev, 'js-previous-button': item.prev,
next: item.next, 'js-next-button': item.next,
'js-last-button': item.last,
'js-first-button': item.first,
separator: item.separator, separator: item.separator,
active: item.active, active: item.active,
disabled: item.disabled disabled: item.disabled
}"> }">
<a @click="changePage($event)">{{item.title}}</a> <a @click.prevent="changePage($event)">{{item.title}}</a>
</li> </li>
</ul> </ul>
</div> </div>
......
...@@ -42,4 +42,4 @@ ...@@ -42,4 +42,4 @@
/* /*
* Styles for JS behaviors. * Styles for JS behaviors.
*/ */
@import "behaviors.scss"; @import "behaviors";
...@@ -4,49 +4,49 @@ ...@@ -4,49 +4,49 @@
@import 'framework/tw_bootstrap'; @import 'framework/tw_bootstrap';
@import "framework/layout"; @import "framework/layout";
@import "framework/animations.scss"; @import "framework/animations";
@import "framework/avatar.scss"; @import "framework/avatar";
@import "framework/asciidoctor.scss"; @import "framework/asciidoctor";
@import "framework/blocks.scss"; @import "framework/blocks";
@import "framework/buttons.scss"; @import "framework/buttons";
@import "framework/badges.scss"; @import "framework/badges";
@import "framework/calendar.scss"; @import "framework/calendar";
@import "framework/callout.scss"; @import "framework/callout";
@import "framework/common.scss"; @import "framework/common";
@import "framework/dropdowns.scss"; @import "framework/dropdowns";
@import "framework/files.scss"; @import "framework/files";
@import "framework/filters.scss"; @import "framework/filters";
@import "framework/flash.scss"; @import "framework/flash";
@import "framework/forms.scss"; @import "framework/forms";
@import "framework/gfm.scss"; @import "framework/gfm";
@import "framework/header.scss"; @import "framework/header";
@import "framework/highlight.scss"; @import "framework/highlight";
@import "framework/issue_box.scss"; @import "framework/issue_box";
@import "framework/jquery.scss"; @import "framework/jquery";
@import "framework/lists.scss"; @import "framework/lists";
@import "framework/logo.scss"; @import "framework/logo";
@import "framework/markdown_area.scss"; @import "framework/markdown_area";
@import "framework/mobile.scss"; @import "framework/mobile";
@import "framework/modal.scss"; @import "framework/modal";
@import "framework/nav.scss"; @import "framework/nav";
@import "framework/pagination.scss"; @import "framework/pagination";
@import "framework/panels.scss"; @import "framework/panels";
@import "framework/selects.scss"; @import "framework/selects";
@import "framework/sidebar.scss"; @import "framework/sidebar";
@import "framework/tables.scss"; @import "framework/tables";
@import "framework/notes.scss"; @import "framework/notes";
@import "framework/timeline.scss"; @import "framework/timeline";
@import "framework/typography.scss"; @import "framework/typography";
@import "framework/zen.scss"; @import "framework/zen";
@import "framework/blank"; @import "framework/blank";
@import "framework/wells.scss"; @import "framework/wells";
@import "framework/page-header.scss"; @import "framework/page-header";
@import "framework/awards.scss"; @import "framework/awards";
@import "framework/images.scss"; @import "framework/images";
@import "framework/broadcast-messages"; @import "framework/broadcast-messages";
@import "framework/emojis.scss"; @import "framework/emojis";
@import "framework/emoji-sprites.scss"; @import "framework/emoji-sprites";
@import "framework/icons.scss"; @import "framework/icons";
@import "framework/snippets.scss"; @import "framework/snippets";
@import "framework/memory_graph.scss"; @import "framework/memory_graph";
@import "framework/responsive-tables.scss"; @import "framework/responsive-tables";
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
opacity: 0; opacity: 0;
transform: scale(.2); transform: scale(.2);
transform-origin: 0 -45px; transform-origin: 0 -45px;
transition: .3s cubic-bezier(.67,.06,.19,1.44); transition: .3s cubic-bezier(.67, .06, .19, 1.44);
transition-property: transform, opacity; transition-property: transform, opacity;
&.is-aligned-right { &.is-aligned-right {
......
.blank-state-welcome { .blank-state-parent-container {
text-align: center; .section-container {
border-bottom: 1px solid $border-color; padding: 10px;
}
.blank-state-text { .section-body {
margin-bottom: 0; width: 100%;
height: 100%;
padding-bottom: 25px;
border: 1px solid $border-color;
border-radius: $border-radius-default;
} }
} }
...@@ -11,41 +16,35 @@ ...@@ -11,41 +16,35 @@
padding-top: 20px; padding-top: 20px;
padding-bottom: 20px; padding-bottom: 20px;
text-align: center; text-align: center;
}
.blank-state-no-icon { &.blank-state-welcome {
padding-top: 40px; .blank-state-welcome-title {
padding-bottom: 40px; font-size: 24px;
} }
.blank-state-icon {
padding-bottom: 20px;
color: $gray-darkest;
font-size: 56px;
path, .blank-state-text {
polygon { margin-bottom: 0;
fill: currentColor; }
} }
}
.blank-state-title { .blank-state-icon {
margin-top: 0; padding-bottom: 20px;
margin-bottom: 5px;
font-size: 18px;
font-weight: normal;
}
.blank-state-text { svg {
margin-top: 0; display: block;
margin-bottom: $gl-padding; margin: auto;
font-size: 14px; }
}
> strong { .blank-state-title {
font-weight: 600; margin-top: 0;
margin-bottom: 10px;
font-size: 18px;
} }
}
.blank-state-welcome-title { .blank-state-text {
font-size: 24px; max-width: $container-text-max-width;
margin: 0 auto $gl-padding;
font-size: 14px;
}
} }
...@@ -336,11 +336,6 @@ table { ...@@ -336,11 +336,6 @@ table {
text-align: center; text-align: center;
} }
#nprogress .spinner {
top: 15px !important;
right: 10px !important;
}
.header-with-avatar { .header-with-avatar {
h3 { h3 {
margin: 0; margin: 0;
...@@ -450,4 +445,3 @@ table { ...@@ -450,4 +445,3 @@ table {
pointer-events: none; pointer-events: none;
opacity: .5; opacity: .5;
} }
...@@ -343,6 +343,12 @@ ul.indent-list { ...@@ -343,6 +343,12 @@ ul.indent-list {
.group-row { .group-row {
padding: 0; padding: 0;
border: none; border: none;
&:last-of-type {
.group-row-contents:not(:hover) {
border-bottom: 1px solid transparent;
}
}
} }
.group-row-contents { .group-row-contents {
......
...@@ -74,11 +74,17 @@ $red-700: #a62d19; ...@@ -74,11 +74,17 @@ $red-700: #a62d19;
$red-800: #8b2615; $red-800: #8b2615;
$red-900: #711e11; $red-900: #711e11;
$purple-600: #6e49cb; $indigo-50: #f7f7ff;
$purple-650: #5c35ae; $indigo-100: #ebebfa;
$purple-700: #4a2192; $indigo-200: #d1d1f0;
$purple-800: #2c0a5c; $indigo-300: #a6a6de;
$purple-900: #380d75; $indigo-400: #7c7ccc;
$indigo-500: #6666c4;
$indigo-600: #5b5bbd;
$indigo-700: #4b4ba3;
$indigo-800: #393982;
$indigo-900: #292961;
$indigo-950: #1a1a40;
$black: #000; $black: #000;
$black-transparent: rgba(0, 0, 0, 0.3); $black-transparent: rgba(0, 0, 0, 0.3);
...@@ -170,6 +176,7 @@ $header-height: 50px; ...@@ -170,6 +176,7 @@ $header-height: 50px;
$fixed-layout-width: 1280px; $fixed-layout-width: 1280px;
$limited-layout-width: 990px; $limited-layout-width: 990px;
$limited-layout-width-sm: 790px; $limited-layout-width-sm: 790px;
$container-text-max-width: 540px;
$gl-avatar-size: 40px; $gl-avatar-size: 40px;
$error-exclamation-point: $red-500; $error-exclamation-point: $red-500;
$border-radius-default: 3px; $border-radius-default: 3px;
...@@ -310,7 +317,7 @@ $badge-color: $gl-text-color-secondary; ...@@ -310,7 +317,7 @@ $badge-color: $gl-text-color-secondary;
/* /*
* Award emoji * Award emoji
*/ */
$award-emoji-menu-shadow: rgba(0,0,0,.175); $award-emoji-menu-shadow: rgba(0, 0, 0, .175);
$award-emoji-positive-add-bg: #fed159; $award-emoji-positive-add-bg: #fed159;
$award-emoji-positive-add-lines: #bb9c13; $award-emoji-positive-add-lines: #bb9c13;
...@@ -561,7 +568,7 @@ $kdb-color: #555; ...@@ -561,7 +568,7 @@ $kdb-color: #555;
$kdb-border: #ccc; $kdb-border: #ccc;
$kdb-border-bottom: #bbb; $kdb-border-bottom: #bbb;
$kdb-shadow: #bbb; $kdb-shadow: #bbb;
$body-text-shadow: rgba(255,255,255,0.01); $body-text-shadow: rgba(255, 255, 255, 0.01);
/* /*
* UI Dev Kit * UI Dev Kit
......
...@@ -113,7 +113,7 @@ $white-gc-bg: #eaf2f5; ...@@ -113,7 +113,7 @@ $white-gc-bg: #eaf2f5;
border-color: $line-removed-dark; border-color: $line-removed-dark;
a { a {
color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%); color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%);
} }
} }
...@@ -122,7 +122,7 @@ $white-gc-bg: #eaf2f5; ...@@ -122,7 +122,7 @@ $white-gc-bg: #eaf2f5;
border-color: $line-added-dark; border-color: $line-added-dark;
a { a {
color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%); color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%);
} }
} }
...@@ -163,7 +163,7 @@ $white-gc-bg: #eaf2f5; ...@@ -163,7 +163,7 @@ $white-gc-bg: #eaf2f5;
background-color: $line-removed; background-color: $line-removed;
&::before { &::before {
color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%); color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%);
} }
span.idiff { span.idiff {
...@@ -175,7 +175,7 @@ $white-gc-bg: #eaf2f5; ...@@ -175,7 +175,7 @@ $white-gc-bg: #eaf2f5;
background-color: $line-added; background-color: $line-added;
&::before { &::before {
color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%); color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%);
} }
span.idiff { span.idiff {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
header.navbar-gitlab-new { header.navbar-gitlab-new {
color: $white-light; color: $white-light;
background-color: $purple-900; background: linear-gradient(to right, $indigo-900, $indigo-800);
border-bottom: 0; border-bottom: 0;
.header-content { .header-content {
...@@ -24,11 +24,9 @@ header.navbar-gitlab-new { ...@@ -24,11 +24,9 @@ header.navbar-gitlab-new {
> a { > a {
display: flex; display: flex;
align-items: center; align-items: center;
padding-top: 3px;
padding-right: $gl-padding; padding-right: $gl-padding;
padding-left: $gl-padding; padding-left: $gl-padding;
margin-left: -$gl-padding; margin-left: -$gl-padding;
border-bottom: 3px solid transparent;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
padding-right: $gl-padding; padding-right: $gl-padding;
...@@ -45,9 +43,8 @@ header.navbar-gitlab-new { ...@@ -45,9 +43,8 @@ header.navbar-gitlab-new {
&:hover, &:hover,
&:focus { &:focus {
color: currentColor; color: $tanuki-yellow;
text-decoration: none; text-decoration: none;
border-bottom-color: $white-light;
} }
} }
} }
...@@ -71,7 +68,7 @@ header.navbar-gitlab-new { ...@@ -71,7 +68,7 @@ header.navbar-gitlab-new {
.navbar-collapse { .navbar-collapse {
padding-left: 0; padding-left: 0;
color: $white-light; color: $indigo-200;
box-shadow: 0; box-shadow: 0;
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
...@@ -101,7 +98,7 @@ header.navbar-gitlab-new { ...@@ -101,7 +98,7 @@ header.navbar-gitlab-new {
font-size: 14px; font-size: 14px;
text-align: center; text-align: center;
color: currentColor; color: currentColor;
border-left: 1px solid lighten($purple-700, 10%); border-left: 1px solid lighten($indigo-700, 10%);
&:hover, &:hover,
&:focus, &:focus,
...@@ -120,6 +117,7 @@ header.navbar-gitlab-new { ...@@ -120,6 +117,7 @@ header.navbar-gitlab-new {
li { li {
.badge { .badge {
box-shadow: none; box-shadow: none;
font-weight: 600;
} }
} }
} }
...@@ -133,12 +131,11 @@ header.navbar-gitlab-new { ...@@ -133,12 +131,11 @@ header.navbar-gitlab-new {
> a { > a {
background: none; background: none;
opacity: .9; will-change: color;
will-change: opacity;
&.header-user-dropdown-toggle { &.header-user-dropdown-toggle {
.header-user-avatar { .header-user-avatar {
border-color: $white-light; border-color: $indigo-200;
} }
} }
...@@ -165,29 +162,34 @@ header.navbar-gitlab-new { ...@@ -165,29 +162,34 @@ header.navbar-gitlab-new {
.navbar-sub-nav { .navbar-sub-nav {
display: flex; display: flex;
margin-bottom: 0; margin-bottom: 0;
color: $white-light; color: $indigo-200;
> li { > li {
&.active > a, > a:hover,
a:hover, > a:focus {
a:focus { box-shadow: inset 0 -3px 0 rgba($indigo-200, .4);
border-bottom-color: $white-light;
text-decoration: none; text-decoration: none;
outline: 0; outline: 0;
opacity: 1; color: $white-light;
}
&.active > a {
box-shadow: inset 0 -3px 0 $indigo-500;
color: $white-light;
font-weight: 700;
} }
> a { > a {
display: block; display: block;
padding: 16px 10px 13px; padding: 16px 10px;
font-size: 13px; font-size: 13px;
color: currentColor; color: currentColor;
border-bottom: 3px solid transparent; box-shadow: inset 0 0 0 transparent;
opacity: .9; will-change: box-shadow;
will-change: opacity; transition: box-shadow 0.15s;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
padding: 15px $gl-padding 12px; padding: 15px $gl-padding;
font-size: 14px; font-size: 14px;
} }
} }
...@@ -207,55 +209,60 @@ header.navbar-gitlab-new { ...@@ -207,55 +209,60 @@ header.navbar-gitlab-new {
.search { .search {
form { form {
border-color: $purple-800; border: 0;
background-color: rgba($indigo-200, .2);
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s, background-color ease-in-out 0.15s;
&:hover { &:hover {
border-color: rgba($white-light, .6); background-color: rgba($indigo-200, .3);
box-shadow: none; box-shadow: none;
} }
} }
&.search-active form { &.search-active form {
border-color: $white-light; background-color: rgba($indigo-200, .3);
} box-shadow: none;
form,
.search-input {
background-color: $purple-700;
} }
.search-input { .search-input {
color: $white-light; color: $white-light;
background: none;
} }
.search-input::placeholder { .search-input::placeholder {
color: rgba($white-light, .6); color: rgba($indigo-200, .8);
} }
.location-badge { .location-badge {
font-size: 12px; font-size: 12px;
color: rgba($white-light, .6); color: $indigo-100;
background-color: $purple-800; background-color: rgba($indigo-200, .1);
transition: color 0.15s; transition: color 0.15s;
will-change: color; will-change: color;
margin: -4px 4px -4px -4px;
line-height: 25px;
padding: 4px 8px;
border-radius: 2px 0 0 2px;
border-right: 1px solid $indigo-800;
height: 34px;
} }
.search-input-wrap { .search-input-wrap {
.search-icon, .search-icon,
.clear-icon { .clear-icon {
color: rgba($white-light, .6); color: rgba($indigo-200, .8);
} }
} }
&.search-active { &.search-active {
.location-badge { .location-badge {
color: $white-light; color: $white-light;
background-color: $purple-800; background-color: rgba($indigo-200, .2);
} }
.search-input-wrap { .search-input-wrap {
.search-icon { .search-icon {
color: rgba($white-light, .6); color: rgba($indigo-200, .8);
} }
.clear-icon { .clear-icon {
......
...@@ -2,6 +2,15 @@ ...@@ -2,6 +2,15 @@
@import 'framework/tw_bootstrap_variables'; @import 'framework/tw_bootstrap_variables';
@import "bootstrap/variables"; @import "bootstrap/variables";
$active-background: rgba(0, 0, 0, .04);
$active-border: $indigo-500;
$active-color: $indigo-700;
$active-hover-background: $active-background;
$active-hover-color: $gl-text-color;
$inactive-badge-background: rgba(0, 0, 0, .08);
$hover-background: $indigo-700;
$hover-color: $white-light;
$inactive-color: $gl-text-color-secondary;
$new-sidebar-width: 220px; $new-sidebar-width: 220px;
.page-with-new-sidebar { .page-with-new-sidebar {
...@@ -17,19 +26,33 @@ $new-sidebar-width: 220px; ...@@ -17,19 +26,33 @@ $new-sidebar-width: 220px;
} }
.context-header { .context-header {
background-color: $gray-normal;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
font-weight: 600; font-weight: 600;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 10px 14px 9px; padding: 10px 16px 10px;
color: $gl-text-color;
.avatar-container { .avatar-container {
flex: 0 0 40px; flex: 0 0 40px;
} }
&:hover { &:hover {
background-color: $border-color; background-color: $hover-background;
color: $hover-color;
border-color: $hover-background;
.avatar-container {
border-color: transparent;
}
.settings-avatar {
background-color: $indigo-500;
i {
color: $hover-color;
}
}
} }
.project-title, .project-title,
...@@ -41,6 +64,7 @@ $new-sidebar-width: 220px; ...@@ -41,6 +64,7 @@ $new-sidebar-width: 220px;
.settings-avatar { .settings-avatar {
background-color: $white-light; background-color: $white-light;
transition: background-color 100ms linear;
i { i {
font-size: 20px; font-size: 20px;
...@@ -48,6 +72,7 @@ $new-sidebar-width: 220px; ...@@ -48,6 +72,7 @@ $new-sidebar-width: 220px;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
text-align: center; text-align: center;
align-self: center; align-self: center;
transition: color 100ms linear;
} }
} }
...@@ -60,11 +85,15 @@ $new-sidebar-width: 220px; ...@@ -60,11 +85,15 @@ $new-sidebar-width: 220px;
bottom: 0; bottom: 0;
left: 0; left: 0;
overflow: auto; overflow: auto;
background-color: $gray-light; background-color: $gray-normal;
border-right: 1px solid $border-color; box-shadow: inset -2px 0 0 $border-color;
a {
text-decoration: none;
}
ul { ul {
padding: 0; padding-left: 0;
list-style: none; list-style: none;
} }
...@@ -73,13 +102,18 @@ $new-sidebar-width: 220px; ...@@ -73,13 +102,18 @@ $new-sidebar-width: 220px;
a { a {
display: block; display: block;
padding: 12px 14px; padding: 12px 16px;
color: $inactive-color;
} }
} }
a { li.active {
color: $gl-text-color; box-shadow: inset 4px 0 0 $active-border;
text-decoration: none;
> a {
color: $active-color;
font-weight: 700;
}
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
...@@ -89,22 +123,28 @@ $new-sidebar-width: 220px; ...@@ -89,22 +123,28 @@ $new-sidebar-width: 220px;
.sidebar-sub-level-items { .sidebar-sub-level-items {
display: none; display: none;
padding-bottom: 8px;
> li { > li {
a { a {
padding: 12px 24px; font-size: 12px;
color: $gl-text-color-light; padding: 8px 16px 8px 24px;
&:hover { &:hover,
color: $gl-text-color; &:focus {
background-color: $border-color; background: $active-hover-background;
color: $active-hover-color;
} }
} }
&.active { &.active {
> a { a {
color: $purple-650; &,
font-weight: 600; &:hover,
&:focus {
background: $active-background;
color: $active-color;
}
} }
} }
} }
...@@ -114,35 +154,31 @@ $new-sidebar-width: 220px; ...@@ -114,35 +154,31 @@ $new-sidebar-width: 220px;
> li { > li {
.badge { .badge {
float: right; float: right;
background-color: $border-color; background-color: $inactive-badge-background;
color: $gl-text-color; color: $inactive-color;
} }
&.active { &.active {
> a { background: $active-background;
background-color: $purple-600;
color: $white-light;
font-weight: 600;
}
.badge { .badge {
background-color: $purple-700; color: $active-color;
color: $white-light; font-weight: 600;
} }
.sidebar-sub-level-items { .sidebar-sub-level-items {
background-color: $gray-normal;
border-left: 6px solid $purple-600;
display: block; display: block;
} }
} }
&:not(.active) > a:hover { > a:hover {
background-color: $border-color; background-color: $hover-background;
color: $hover-color;
.badge { .badge {
transition: background-color 100ms linear; transition: background-color 100ms linear, color 100ms linear;
background-color: $gray-normal; background-color: $indigo-500;
color: $hover-color;
} }
} }
} }
...@@ -161,3 +197,13 @@ $new-sidebar-width: 220px; ...@@ -161,3 +197,13 @@ $new-sidebar-width: 220px;
// scss-lint:enable DuplicateProperty // scss-lint:enable DuplicateProperty
} }
} }
// Change color of all horizontal tabs to match the new indigo color
.nav-links li.active a {
border-bottom-color: $active-border;
.badge {
font-weight: 600;
}
}
...@@ -6,26 +6,26 @@ ...@@ -6,26 +6,26 @@
@keyframes blinking-dots { @keyframes blinking-dots {
0% { 0% {
background-color: rgba($white-light, 1); background-color: rgba($white-light, 1);
box-shadow: 12px 0 0 0 rgba($white-light,0.2), box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
24px 0 0 0 rgba($white-light,0.2); 24px 0 0 0 rgba($white-light, 0.2);
} }
25% { 25% {
background-color: rgba($white-light, 0.4); background-color: rgba($white-light, 0.4);
box-shadow: 12px 0 0 0 rgba($white-light,2), box-shadow: 12px 0 0 0 rgba($white-light, 2),
24px 0 0 0 rgba($white-light,0.2); 24px 0 0 0 rgba($white-light, 0.2);
} }
75% { 75% {
background-color: rgba($white-light, 0.4); background-color: rgba($white-light, 0.4);
box-shadow: 12px 0 0 0 rgba($white-light,0.2), box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
24px 0 0 0 rgba($white-light,1); 24px 0 0 0 rgba($white-light, 1);
} }
100% { 100% {
background-color: rgba($white-light, 1); background-color: rgba($white-light, 1);
box-shadow: 12px 0 0 0 rgba($white-light,0.2), box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
24px 0 0 0 rgba($white-light,0.2); 24px 0 0 0 rgba($white-light, 0.2);
} }
} }
......
...@@ -54,8 +54,6 @@ ...@@ -54,8 +54,6 @@
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
width: 400px;
max-width: 50%;
} }
} }
...@@ -65,7 +63,6 @@ ...@@ -65,7 +63,6 @@
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
width: 100%;
margin-top: 3px; margin-top: 3px;
} }
} }
...@@ -81,18 +78,10 @@ ...@@ -81,18 +78,10 @@
.member-form-control { .member-form-control {
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
padding: 5px 0; padding-bottom: 5px;
margin-left: 0; margin-left: 0;
margin-right: 0; margin-right: 0;
} }
@media (min-width: $screen-sm-min) {
width: 50%;
}
.dropdown-menu-toggle {
width: 100%;
}
} }
.member-access-text { .member-access-text {
...@@ -216,3 +205,102 @@ ...@@ -216,3 +205,102 @@
} }
} }
} }
.content-list.members-list li {
display: flex;
justify-content: space-between;
.list-item-name {
float: none;
display: flex;
flex: 1;
}
.user-info {
padding-right: 10px;
}
.member {
font-weight: bold;
overflow-wrap: break-word;
word-break: break-all;
}
.member-group-link {
display: inline-block;
}
.form-control {
width: inherit;
}
.btn {
align-self: flex-start;
}
.form-horizontal ~ .btn {
margin-right: 0;
}
@media (max-width: $screen-xs-max) {
display: block;
.controls > .btn {
margin-left: 0;
margin-right: 0;
display: block;
}
.form-control {
width: 100%;
}
.member-access-text {
line-height: 0;
margin-left: 50px;
}
.member-controls {
margin-top: 5px;
}
.form-horizontal {
margin-top: 10px;
}
}
}
.panel-mobile {
.content-list.members-list li {
display: block;
.member-controls {
float: none;
display: block;
}
.dropdown-menu-toggle,
.dropdown-menu,
.form-control,
.list-item-name {
width: 100%;
}
.dropdown-menu {
margin-top: 0;
}
.form-horizontal {
display: block;
}
.member-form-control {
margin: 5px 0;
}
.btn {
width: 100%;
margin-left: 0;
}
}
}
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
.dropdown-menu { .dropdown-menu {
margin-top: 11px; margin-top: 11px;
z-index: 200; z-index: 300;
} }
.ci-action-icon-wrapper { .ci-action-icon-wrapper {
......
...@@ -286,8 +286,7 @@ table.u2f-registrations { ...@@ -286,8 +286,7 @@ table.u2f-registrations {
} }
.user-callout { .user-callout {
margin: 0 auto; margin: 20px -5px 0;
max-width: $screen-lg-min;
.bordered-box { .bordered-box {
border: 1px solid $blue-300; border: 1px solid $blue-300;
......
module IssuableCollections module IssuableCollections
extend ActiveSupport::Concern extend ActiveSupport::Concern
include SortingHelper include SortingHelper
include Gitlab::IssuableMetadata
included do included do
helper_method :issues_finder helper_method :issues_finder
...@@ -9,39 +10,6 @@ module IssuableCollections ...@@ -9,39 +10,6 @@ module IssuableCollections
private private
def issuable_meta_data(issuable_collection, collection_type)
# map has to be used here since using pluck or select will
# throw an error when ordering issuables by priority which inserts
# a new order into the collection.
# We cannot use reorder to not mess up the paginated collection.
issuable_ids = issuable_collection.map(&:id)
return {} if issuable_ids.empty?
issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type)
issuable_votes_count = AwardEmoji.votes_for_collection(issuable_ids, @collection_type)
issuable_merge_requests_count =
if collection_type == 'Issue'
MergeRequestsClosingIssues.count_for_collection(issuable_ids)
else
[]
end
issuable_ids.each_with_object({}) do |id, issuable_meta|
downvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.downvote? }
upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? }
notes = issuable_note_count.find { |notes| notes.noteable_id == id }
merge_requests = issuable_merge_requests_count.find { |mr| mr.first == id }
issuable_meta[id] = Issuable::IssuableMeta.new(
upvotes.try(:count).to_i,
downvotes.try(:count).to_i,
notes.try(:count).to_i,
merge_requests.try(:last).to_i
)
end
end
def issues_collection def issues_collection
issues_finder.execute.preload(:project, :author, :assignees, :labels, :milestone, project: :namespace) issues_finder.execute.preload(:project, :author, :assignees, :labels, :milestone, project: :namespace)
end end
......
module RequiresHealthToken module RequiresWhitelistedMonitoringClient
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
before_action :validate_health_check_access! before_action :validate_ip_whitelisted_or_valid_token!
end end
private private
def validate_health_check_access! def validate_ip_whitelisted_or_valid_token!
render_404 unless token_valid? render_404 unless client_ip_whitelisted? || valid_token?
end end
def token_valid? def client_ip_whitelisted?
ip_whitelist.any? { |e| e.include?(Gitlab::RequestContext.client_ip) }
end
def ip_whitelist
@ip_whitelist ||= Settings.monitoring.ip_whitelist.map(&IPAddr.method(:new))
end
def valid_token?
token = params[:token].presence || request.headers['TOKEN'] token = params[:token].presence || request.headers['TOKEN']
token.present? && token.present? &&
ActiveSupport::SecurityUtils.variable_size_secure_compare( ActiveSupport::SecurityUtils.variable_size_secure_compare(
......
class HealthCheckController < HealthCheck::HealthCheckController class HealthCheckController < HealthCheck::HealthCheckController
include RequiresHealthToken include RequiresWhitelistedMonitoringClient
end end
class HealthController < ActionController::Base class HealthController < ActionController::Base
protect_from_forgery with: :exception protect_from_forgery with: :exception
include RequiresHealthToken include RequiresWhitelistedMonitoringClient
CHECKS = [ CHECKS = [
Gitlab::HealthChecks::DbCheck, Gitlab::HealthChecks::DbCheck,
Gitlab::HealthChecks::RedisCheck, Gitlab::HealthChecks::Redis::RedisCheck,
Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck,
Gitlab::HealthChecks::FsShardsCheck Gitlab::HealthChecks::FsShardsCheck
].freeze ].freeze
......
class MetricsController < ActionController::Base class MetricsController < ActionController::Base
include RequiresHealthToken include RequiresWhitelistedMonitoringClient
protect_from_forgery with: :exception protect_from_forgery with: :exception
before_action :validate_prometheus_metrics before_action :validate_prometheus_metrics
def index def index
render text: metrics_service.metrics_text, content_type: 'text/plain; verssion=0.0.4' render text: metrics_service.metrics_text, content_type: 'text/plain; version=0.0.4'
end end
private private
......
module CreatedAtFilter
def by_created_at(items)
items = items.created_before(params[:created_before]) if params[:created_before].present?
items = items.created_after(params[:created_after]) if params[:created_after].present?
items
end
end
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
# iids: integer[] # iids: integer[]
# #
class IssuableFinder class IssuableFinder
include CreatedAtFilter
NONE = '0'.freeze NONE = '0'.freeze
IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze
...@@ -32,6 +34,7 @@ class IssuableFinder ...@@ -32,6 +34,7 @@ class IssuableFinder
def execute def execute
items = init_collection items = init_collection
items = by_scope(items) items = by_scope(items)
items = by_created_at(items)
items = by_state(items) items = by_state(items)
items = by_group(items) items = by_group(items)
items = by_search(items) items = by_search(items)
...@@ -42,7 +45,6 @@ class IssuableFinder ...@@ -42,7 +45,6 @@ class IssuableFinder
items = by_iids(items) items = by_iids(items)
items = by_milestone(items) items = by_milestone(items)
items = by_label(items) items = by_label(items)
items = by_created_at(items)
# Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far
items = by_project(items) items = by_project(items)
...@@ -411,18 +413,6 @@ class IssuableFinder ...@@ -411,18 +413,6 @@ class IssuableFinder
params[:non_archived].present? ? items.non_archived : items params[:non_archived].present? ? items.non_archived : items
end end
def by_created_at(items)
if params[:created_after].present?
items = items.where(items.klass.arel_table[:created_at].gteq(params[:created_after]))
end
if params[:created_before].present?
items = items.where(items.klass.arel_table[:created_at].lteq(params[:created_before]))
end
items
end
def current_user_related? def current_user_related?
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end end
......
...@@ -49,7 +49,8 @@ class MilestonesFinder ...@@ -49,7 +49,8 @@ class MilestonesFinder
if params.has_key?(:order) if params.has_key?(:order)
items.reorder(params[:order]) items.reorder(params[:order])
else else
items.reorder('due_date ASC') order_statement = Gitlab::Database.nulls_last_order('due_date', 'ASC')
items.reorder(order_statement)
end end
end end
end end
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
# external: boolean # external: boolean
# #
class UsersFinder class UsersFinder
include CreatedAtFilter
attr_accessor :current_user, :params attr_accessor :current_user, :params
def initialize(current_user, params = {}) def initialize(current_user, params = {})
...@@ -29,6 +31,7 @@ class UsersFinder ...@@ -29,6 +31,7 @@ class UsersFinder
users = by_active(users) users = by_active(users)
users = by_external_identity(users) users = by_external_identity(users)
users = by_external(users) users = by_external(users)
users = by_created_at(users)
users users
end end
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
module GitlabRoutingHelper module GitlabRoutingHelper
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do
Gitlab::Routing.includes_helpers(self)
end
# Project # Project
def project_tree_path(project, ref = nil, *args) def project_tree_path(project, ref = nil, *args)
namespace_project_tree_path(project.namespace, project, ref || @ref || project.repository.root_ref, *args) # rubocop:disable Cop/ProjectPathHelper namespace_project_tree_path(project.namespace, project, ref || @ref || project.repository.root_ref, *args) # rubocop:disable Cop/ProjectPathHelper
......
...@@ -184,6 +184,9 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -184,6 +184,9 @@ class ApplicationSetting < ActiveRecord::Base
Rails.cache.fetch(CACHE_KEY) do Rails.cache.fetch(CACHE_KEY) do
ApplicationSetting.last ApplicationSetting.last
end end
rescue
# Fall back to an uncached value if there are any problems (e.g. redis down)
ApplicationSetting.last
end end
def self.expire def self.expire
......
...@@ -3,7 +3,7 @@ module Ci ...@@ -3,7 +3,7 @@ module Ci
extend Ci::Model extend Ci::Model
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
LAST_CONTACT_TIME = 1.hour.ago ONLINE_CONTACT_TIMEOUT = 1.hour
AVAILABLE_SCOPES = %w[specific shared active paused online].freeze AVAILABLE_SCOPES = %w[specific shared active paused online].freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze
...@@ -19,7 +19,7 @@ module Ci ...@@ -19,7 +19,7 @@ module Ci
scope :shared, ->() { where(is_shared: true) } scope :shared, ->() { where(is_shared: true) }
scope :active, ->() { where(active: true) } scope :active, ->() { where(active: true) }
scope :paused, ->() { where(active: false) } scope :paused, ->() { where(active: false) }
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } scope :online, ->() { where('contacted_at > ?', contact_time_deadline) }
scope :ordered, ->() { order(id: :desc) } scope :ordered, ->() { order(id: :desc) }
scope :owned_or_shared, ->(project_id) do scope :owned_or_shared, ->(project_id) do
...@@ -59,6 +59,10 @@ module Ci ...@@ -59,6 +59,10 @@ module Ci
where(t[:token].matches(pattern).or(t[:description].matches(pattern))) where(t[:token].matches(pattern).or(t[:description].matches(pattern)))
end end
def self.contact_time_deadline
ONLINE_CONTACT_TIMEOUT.ago
end
def set_default_values def set_default_values
self.token = SecureRandom.hex(15) if self.token.blank? self.token = SecureRandom.hex(15) if self.token.blank?
end end
...@@ -80,7 +84,7 @@ module Ci ...@@ -80,7 +84,7 @@ module Ci
end end
def online? def online?
contacted_at && contacted_at > LAST_CONTACT_TIME contacted_at && contacted_at > self.class.contact_time_deadline
end end
def status def status
...@@ -145,7 +149,7 @@ module Ci ...@@ -145,7 +149,7 @@ module Ci
private private
def cleanup_runner_queue def cleanup_runner_queue
Gitlab::Redis.with do |redis| Gitlab::Redis::Queues.with do |redis|
redis.del(runner_queue_key) redis.del(runner_queue_key)
end end
end end
......
module CreatedAtFilterable
extend ActiveSupport::Concern
included do
scope :created_before, ->(date) { where(scoped_table[:created_at].lteq(date)) }
scope :created_after, ->(date) { where(scoped_table[:created_at].gteq(date)) }
def self.scoped_table
arel_table.alias(table_name)
end
end
end
...@@ -10,6 +10,7 @@ class Issue < ActiveRecord::Base ...@@ -10,6 +10,7 @@ class Issue < ActiveRecord::Base
include FasterCacheKeys include FasterCacheKeys
include RelativePositioning include RelativePositioning
include IgnorableColumn include IgnorableColumn
include CreatedAtFilterable
ignore_column :position ignore_column :position
...@@ -50,8 +51,6 @@ class Issue < ActiveRecord::Base ...@@ -50,8 +51,6 @@ class Issue < ActiveRecord::Base
scope :order_due_date_asc, -> { reorder('issues.due_date IS NULL, issues.due_date ASC') } scope :order_due_date_asc, -> { reorder('issues.due_date IS NULL, issues.due_date ASC') }
scope :order_due_date_desc, -> { reorder('issues.due_date IS NULL, issues.due_date DESC') } scope :order_due_date_desc, -> { reorder('issues.due_date IS NULL, issues.due_date DESC') }
scope :created_after, -> (datetime) { where("created_at >= ?", datetime) }
scope :preload_associations, -> { preload(:labels, project: :namespace) } scope :preload_associations, -> { preload(:labels, project: :namespace) }
after_save :expire_etag_cache after_save :expire_etag_cache
......
...@@ -5,6 +5,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -5,6 +5,7 @@ class MergeRequest < ActiveRecord::Base
include Referable include Referable
include Sortable include Sortable
include IgnorableColumn include IgnorableColumn
include CreatedAtFilterable
ignore_column :position ignore_column :position
...@@ -14,7 +15,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -14,7 +15,7 @@ class MergeRequest < ActiveRecord::Base
has_many :merge_request_diffs has_many :merge_request_diffs
has_one :merge_request_diff, has_one :merge_request_diff,
-> { order('merge_request_diffs.id DESC') } -> { order('merge_request_diffs.id DESC') }, inverse_of: :merge_request
belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline" belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline"
......
...@@ -536,6 +536,11 @@ class Project < ActiveRecord::Base ...@@ -536,6 +536,11 @@ class Project < ActiveRecord::Base
ProjectCacheWorker.perform_async(self.id) ProjectCacheWorker.perform_async(self.id)
end end
remove_import_data
end
# This method is overriden in EE::Project model
def remove_import_data
import_data&.destroy import_data&.destroy
end end
...@@ -796,10 +801,12 @@ class Project < ActiveRecord::Base ...@@ -796,10 +801,12 @@ class Project < ActiveRecord::Base
update_column(:has_external_wiki, services.external_wikis.any?) update_column(:has_external_wiki, services.external_wikis.any?)
end end
def find_or_initialize_services def find_or_initialize_services(exceptions: [])
services_templates = Service.where(template: true) services_templates = Service.where(template: true)
Service.available_services_names.map do |service_name| available_services_names = Service.available_services_names - exceptions
available_services_names.map do |service_name|
service = find_service(services, service_name) service = find_service(services, service_name)
if service if service
...@@ -1379,15 +1386,15 @@ class Project < ActiveRecord::Base ...@@ -1379,15 +1386,15 @@ class Project < ActiveRecord::Base
end end
def pushes_since_gc def pushes_since_gc
Gitlab::Redis.with { |redis| redis.get(pushes_since_gc_redis_key).to_i } Gitlab::Redis::SharedState.with { |redis| redis.get(pushes_since_gc_redis_shared_state_key).to_i }
end end
def increment_pushes_since_gc def increment_pushes_since_gc
Gitlab::Redis.with { |redis| redis.incr(pushes_since_gc_redis_key) } Gitlab::Redis::SharedState.with { |redis| redis.incr(pushes_since_gc_redis_shared_state_key) }
end end
def reset_pushes_since_gc def reset_pushes_since_gc
Gitlab::Redis.with { |redis| redis.del(pushes_since_gc_redis_key) } Gitlab::Redis::SharedState.with { |redis| redis.del(pushes_since_gc_redis_shared_state_key) }
end end
def route_map_for(commit_sha) def route_map_for(commit_sha)
...@@ -1450,7 +1457,7 @@ class Project < ActiveRecord::Base ...@@ -1450,7 +1457,7 @@ class Project < ActiveRecord::Base
from && self != from from && self != from
end end
def pushes_since_gc_redis_key def pushes_since_gc_redis_shared_state_key
"projects/#{id}/pushes_since_gc" "projects/#{id}/pushes_since_gc"
end end
......
...@@ -12,6 +12,7 @@ class User < ActiveRecord::Base ...@@ -12,6 +12,7 @@ class User < ActiveRecord::Base
include TokenAuthenticatable include TokenAuthenticatable
include IgnorableColumn include IgnorableColumn
include FeatureGate include FeatureGate
include CreatedAtFilterable
DEFAULT_NOTIFICATION_LEVEL = :participating DEFAULT_NOTIFICATION_LEVEL = :participating
......
...@@ -3,7 +3,10 @@ require 'prometheus/client/formats/text' ...@@ -3,7 +3,10 @@ require 'prometheus/client/formats/text'
class MetricsService class MetricsService
CHECKS = [ CHECKS = [
Gitlab::HealthChecks::DbCheck, Gitlab::HealthChecks::DbCheck,
Gitlab::HealthChecks::RedisCheck, Gitlab::HealthChecks::Redis::RedisCheck,
Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck,
Gitlab::HealthChecks::FsShardsCheck Gitlab::HealthChecks::FsShardsCheck
].freeze ].freeze
......
module Milestones module Milestones
class DestroyService < Milestones::BaseService class DestroyService < Milestones::BaseService
def execute(milestone) def execute(milestone)
return unless milestone.is_project_milestone?
Milestone.transaction do Milestone.transaction do
update_params = { milestone: nil } update_params = { milestone: nil }
milestone.issues.each do |issue| milestone.issues.each do |issue|
Issues::UpdateService.new(project, current_user, update_params).execute(issue) Issues::UpdateService.new(parent, current_user, update_params).execute(issue)
end end
milestone.merge_requests.each do |merge_request| milestone.merge_requests.each do |merge_request|
MergeRequests::UpdateService.new(project, current_user, update_params).execute(merge_request) MergeRequests::UpdateService.new(parent, current_user, update_params).execute(merge_request)
end end
event_service.destroy_milestone(milestone, current_user) event_service.destroy_milestone(milestone, current_user)
......
...@@ -107,8 +107,7 @@ ...@@ -107,8 +107,7 @@
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2" = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr %hr
= button_tag 'Add users to group', class: "btn btn-create" = button_tag 'Add users to group', class: "btn btn-create"
= render 'shared/members/requests', membership_source: @group, requesters: @requesters, force_mobile_view: true
= render 'shared/members/requests', membership_source: @group, requesters: @requesters
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
...@@ -117,7 +116,7 @@ ...@@ -117,7 +116,7 @@
%span.badge= @group.members.size %span.badge= @group.members.size
.pull-right .pull-right
= link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@group, :members]), class: "btn btn-xs" = link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@group, :members]), class: "btn btn-xs"
%ul.well-list.group-users-list.content-list %ul.well-list.group-users-list.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member, locals: { show_controls: false } = render partial: 'shared/members/member', collection: @members, as: :member, locals: { show_controls: false }
.panel-footer .panel-footer
= paginate @members, param_name: 'members_page', theme: 'gitlab' = paginate @members, param_name: 'members_page', theme: 'gitlab'
...@@ -160,12 +160,12 @@ ...@@ -160,12 +160,12 @@
.pull-right .pull-right
= link_to admin_group_path(@group), class: 'btn btn-xs' do = link_to admin_group_path(@group), class: 'btn btn-xs' do
= icon('pencil-square-o', text: 'Manage access') = icon('pencil-square-o', text: 'Manage access')
%ul.well-list.content-list %ul.well-list.content-list.members-list
= render partial: 'shared/members/member', collection: @group_members, as: :member, locals: { show_controls: false } = render partial: 'shared/members/member', collection: @group_members, as: :member, locals: { show_controls: false }
.panel-footer .panel-footer
= paginate @group_members, param_name: 'group_members_page', theme: 'gitlab' = paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
= render 'shared/members/requests', membership_source: @project, requesters: @requesters = render 'shared/members/requests', membership_source: @project, requesters: @requesters, force_mobile_view: true
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
...@@ -174,7 +174,7 @@ ...@@ -174,7 +174,7 @@
%span.badge= @project.users.size %span.badge= @project.users.size
.pull-right .pull-right
= link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@project, :members]), class: "btn btn-xs" = link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@project, :members]), class: "btn btn-xs"
%ul.well-list.project_members.content-list %ul.well-list.project_members.content-list.members-list
= render partial: 'shared/members/member', collection: @project_members, as: :member, locals: { show_controls: false } = render partial: 'shared/members/member', collection: @project_members, as: :member, locals: { show_controls: false }
.panel-footer .panel-footer
= paginate @project_members, param_name: 'project_members_page', theme: 'gitlab' = paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
.blank-state
.blank-state-icon
= custom_icon("add_new_user", size: 50)
.blank-state-body
%h3.blank-state-title
Add user
%p.blank-state-text
Add your team members and others to GitLab.
= link_to new_admin_user_path, class: "btn btn-new" do
New user
.blank-state
.blank-state-icon
= custom_icon("configure_server", size: 50)
.blank-state-body
%h3.blank-state-title
Configure GitLab
%p.blank-state-text
Make adjustments to how your GitLab instance is set up.
= link_to admin_root_path, class: "btn btn-new" do
Configure
- if current_user.can_create_group?
.blank-state
.blank-state-icon
= custom_icon("add_new_group", size: 50)
.blank-state-body
%h3.blank-state-title
Create a group
%p.blank-state-text
Groups are a great way to organise projects and people.
= link_to new_group_path, class: "btn btn-new" do
New group
- public_project_count = ProjectsFinder.new(current_user: current_user).execute.count
- if current_user.can_create_group?
.blank-state
.blank-state-icon
= custom_icon("add_new_group", size: 50)
.blank-state-body
%h3.blank-state-title
Create a group for several dependent projects.
%p.blank-state-text
Groups are the best way to manage projects and members.
= link_to new_group_path, class: "btn btn-new" do
New group
.blank-state
.blank-state-icon
= custom_icon("add_new_project", size: 50)
.blank-state-body
%h3.blank-state-title
Create a project
%p.blank-state-text
- if current_user.can_create_project?
You don't have access to any projects right now.
You can create up to
%strong= number_with_delimiter(current_user.projects_limit)
= succeed "." do
= "project".pluralize(current_user.projects_limit)
- else
If you are added to a project, it will be displayed here.
- if current_user.can_create_project?
= link_to new_project_path, class: "btn btn-new" do
New project
- if public_project_count > 0
.blank-state
.blank-state-icon
= custom_icon("globe", size: 50)
.blank-state-body
%h3.blank-state-title
Explore public projects
%p.blank-state-text
There are
= number_with_delimiter(public_project_count)
public projects on this server.
Public projects are an easy way to allow
everyone to have read-only access.
= link_to trending_explore_projects_path, class: "btn btn-new" do
Browse projects
- publicish_project_count = ProjectsFinder.new(current_user: current_user).execute.count .row.blank-state-parent-container
.blank-state.blank-state-welcome .section-container.section-welcome{ class: "#{ 'section-admin-welcome' if current_user.admin? }" }
%h2.blank-state-welcome-title .container.section-body
Welcome to GitLab .blank-state.blank-state-welcome
%p.blank-state-text %h2.blank-state-welcome-title
Code, test, and deploy together Welcome to GitLab
%p.blank-state-text
- if current_user.can_create_group? Code, test, and deploy together
.blank-state - if current_user.admin?
.blank-state-icon = render "blank_state_admin_welcome"
= custom_icon("group", size: 50) - else
%h3.blank-state-title = render "blank_state_welcome"
You can create a group for several dependent projects.
%p.blank-state-text
Groups are the best way to manage projects and members.
= link_to new_group_path, class: "btn btn-new" do
New group
.blank-state
.blank-state-icon
= custom_icon("project", size: 50)
%h3.blank-state-title
You don't have access to any projects right now
%p.blank-state-text
- if current_user.can_create_project?
You can create up to
%strong= number_with_delimiter(current_user.projects_limit)
= succeed "." do
= "project".pluralize(current_user.projects_limit)
- else
If you are added to a project, it will be displayed here.
- if current_user.can_create_project?
= link_to new_project_path, class: "btn btn-new" do
New project
- if publicish_project_count > 0
.blank-state
.blank-state-icon
= icon("globe")
%h3.blank-state-title
There are
= number_with_delimiter(publicish_project_count)
public projects on this server.
%p.blank-state-text
Public projects are an easy way to allow everyone to have read-only access.
= link_to trending_explore_projects_path, class: "btn btn-new" do
Browse projects
...@@ -28,6 +28,6 @@ ...@@ -28,6 +28,6 @@
Members with access to Members with access to
%strong= @group.name %strong= @group.name
%span.badge= @members.total_count %span.badge= @members.total_count
%ul.content-list %ul.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member = render partial: 'shared/members/member', collection: @members, as: :member
= paginate @members, theme: 'gitlab' = paginate @members, theme: 'gitlab'
...@@ -525,7 +525,7 @@ ...@@ -525,7 +525,7 @@
%h4 %h4
%code .file-holder %code .file-holder
- blob = Snippet.new(content: "Wow\nSuch\nFile") - blob = Snippet.new(content: "Wow\nSuch\nFile").blob
.example .example
.file-holder .file-holder
.js-file-title.file-title .js-file-title.file-title
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- hide_top_links = @hide_top_links || false - hide_top_links = @hide_top_links || false
%nav.breadcrumbs{ role: "navigation" } %nav.breadcrumbs{ role: "navigation" }
.breadcrumbs-container{ class: container_class } .breadcrumbs-container{ class: [container_class, @content_class] }
.breadcrumbs-links.js-title-container .breadcrumbs-links.js-title-container
- unless hide_top_links - unless hide_top_links
.title .title
......
...@@ -74,9 +74,9 @@ ...@@ -74,9 +74,9 @@
= nav_link(controller: @project.default_issues_tracker? ? [:issues, :labels, :milestones, :boards] : :issues) do = nav_link(controller: @project.default_issues_tracker? ? [:issues, :labels, :milestones, :boards] : :issues) do
= link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do = link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do
%span %span
Issues
- if @project.default_issues_tracker? - if @project.default_issues_tracker?
%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(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
Issues
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
- if project_nav_tab?(:issues) && !current_controller?(:merge_requests) - if project_nav_tab?(:issues) && !current_controller?(:merge_requests)
...@@ -112,8 +112,8 @@ ...@@ -112,8 +112,8 @@
= nav_link(controller: @project.default_issues_tracker? ? :merge_requests : [:merge_requests, :labels, :milestones]) do = nav_link(controller: @project.default_issues_tracker? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
= link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%span %span
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(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
Merge Requests
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
......
- @no_container = true - @no_container = true
- container_class = !fluid_layout && diff_view == :inline ? 'container-limited' : '' - container_class = !fluid_layout && diff_view == :inline ? 'container-limited' : ''
- limited_container_width = fluid_layout ? '' : 'limit-container-width' - limited_container_width = fluid_layout ? '' : 'limit-container-width'
- @content_class = limited_container_width
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
- page_description @commit.description - page_description @commit.description
= render "projects/commits/head" = render "projects/commits/head"
......
...@@ -11,8 +11,9 @@ ...@@ -11,8 +11,9 @@
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
%h4.prepend-top-0 %h4.prepend-top-0
New project New project
%p - if import_sources_enabled?
Create or Import your project from popular Git services %p
Create or Import your project from popular Git services
.col-lg-9 .col-lg-9
= form_for @project, html: { class: 'new_project' } do |f| = form_for @project, html: { class: 'new_project' } do |f|
.row .row
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- @no_container = true - @no_container = true
- page_title _("Pipeline Schedules") - page_title _("Pipeline Schedules")
- if show_new_nav? - if show_new_nav? && can?(current_user, :create_pipeline_schedule, @project)
- content_for :breadcrumbs_extra do - content_for :breadcrumbs_extra do
= link_to _('New schedule'), new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create' = link_to _('New schedule'), new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create'
...@@ -21,9 +21,10 @@ ...@@ -21,9 +21,10 @@
- schedule_path_proc = ->(scope) { pipeline_schedules_path(@project, scope: scope) } - schedule_path_proc = ->(scope) { pipeline_schedules_path(@project, scope: scope) }
= render "tabs", schedule_path_proc: schedule_path_proc, all_schedules: @all_schedules, scope: @scope = render "tabs", schedule_path_proc: schedule_path_proc, all_schedules: @all_schedules, scope: @scope
.nav-controls{ class: ("visible-xs" if show_new_nav?) } - if can?(current_user, :create_pipeline_schedule, @project)
= link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do .nav-controls{ class: ("visible-xs" if show_new_nav?) }
%span= _('New schedule') = link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do
%span= _('New schedule')
- if @schedules.present? - if @schedules.present?
%ul.content-list %ul.content-list
......
...@@ -11,5 +11,5 @@ ...@@ -11,5 +11,5 @@
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search") = icon("search")
= render 'shared/members/sort_dropdown' = render 'shared/members/sort_dropdown'
%ul.content-list %ul.content-list.members-list
= render partial: 'shared/members/member', collection: members, as: :member = render partial: 'shared/members/member', collection: members, as: :member
- @no_container = true - @no_container = true
- @breadcrumb_title = "Project" - @breadcrumb_title = "Project"
- @content_class = "limit-container-width" unless fluid_layout
- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message - flash_message_container = show_new_nav? ? :new_global_flash : :flash_message
= content_for :meta_tags do = content_for :meta_tags do
......
- @no_container = true - @no_container = true
- @breadcrumb_title = _("Repository") - @breadcrumb_title = _("Repository")
- @content_class = "limit-container-width" unless fluid_layout
- page_title @path.presence || _("Files"), @ref - page_title @path.presence || _("Files"), @ref
= content_for :meta_tags do = content_for :meta_tags do
......
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82">
<g fill="none" fill-rule="evenodd">
<path fill="#000" fill-opacity=".03" d="M2.12 42C2.04 43 2 44 2 45c0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3C74.35 61.03 58.42 76 39 76S3.65 61.03 2.12 42z"/>
<path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/>
<path fill="#E1DBF2" fill-rule="nonzero" d="M59.65 32.65H60l-2-2.42-2 2.4-2-2.4-2 2.4-2-2.4-2 2.4-2-2.4-2 2.42h.77C45.57 34.6 46 36.75 46 39c0 2.84-.7 5.5-1.92 7.86 1.97 2.28 4.83 3.64 7.92 3.64 5.8 0 10.5-4.74 10.5-10.6 0-2.8-1.08-5.36-2.85-7.25zM43.18 29.6c2.4-2.1 5.52-3.3 8.82-3.3 7.46 0 13.5 6.1 13.5 13.6S59.46 53.5 52 53.5c-3.68 0-7.1-1.5-9.6-4.04C39.3 53.44 34.44 56 29 56c-9.4 0-17-7.6-17-17s7.6-17 17-17c3.22 0 6.23.9 8.8 2.45 2.13 1.3 3.97 3.05 5.38 5.16zM17 34c-.65 1.54-1 3.23-1 5 0 7.18 5.82 13 13 13s13-5.82 13-13c0-1.77-.35-3.46-1-5h-9c-.53 0-1.04-.2-1.4-.6L29 31.84l-1.6 1.58c-.36.4-.87.6-1.4.6h-9zm21.38-4c-2.4-2.5-5.76-4-9.38-4-3.62 0-6.98 1.5-9.38 4h5.55l2.42-2.4c.74-.8 2-.8 2.8 0l2.4 2.4h5.54z"/>
<path fill="#6B4FBB" d="M47.6 42.32c-.66 0-1.2-.54-1.2-1.2 0-.68.54-1.22 1.2-1.22.66 0 1.2.54 1.2 1.2 0 .68-.54 1.22-1.2 1.22zm8.8 0c-.66 0-1.2-.54-1.2-1.2 0-.68.54-1.22 1.2-1.22.66 0 1.2.54 1.2 1.2 0 .68-.54 1.22-1.2 1.22zM25 44h8c0 2.2-1.8 4-4 4s-4-1.8-4-4zm-1.5-1c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"><g fill="none" fill-rule="evenodd"><path fill="#F9F9F9" d="M2.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3C74.353 61.032 58.425 76 39 76 19.575 76 3.647 61.032 2.12 42z"/><path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/><path fill="#FEE1D3" fill-rule="nonzero" d="M30 24a4 4 0 0 0-4 4v22a4 4 0 0 0 4 4h18a4 4 0 0 0 4-4V28a4 4 0 0 0-4-4H30zm0-4h18a8 8 0 0 1 8 8v22a8 8 0 0 1-8 8H30a8 8 0 0 1-8-8V28a8 8 0 0 1 8-8z"/><path fill="#FC6D26" d="M33 30h8a2 2 0 1 1 0 4h-8a2 2 0 1 1 0-4zm0 7h12a2 2 0 1 1 0 4H33a2 2 0 1 1 0-4zm0 7h12a2 2 0 1 1 0 4H33a2 2 0 1 1 0-4z"/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82">
<g fill="none" fill-rule="evenodd">
<path fill="#000" fill-opacity=".03" d="M2.12 42C2.04 43 2 44 2 45c0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3C74.35 61.03 58.42 76 39 76S3.65 61.03 2.12 42z"/>
<path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/>
<path fill="#E1DBF2" d="M44 31l-2.5-3-2.5 3-2.5-3-2.5 3-2.5-3-2.5 3h-2.72c2.65-4.2 7.36-7 12.72-7s10.07 2.8 12.72 7H49l-2.5-3-2.5 3z"/>
<path fill="#E1DBF2" fill-rule="nonzero" d="M39 57c-9.4 0-17-7.6-17-17s7.6-17 17-17 17 7.6 17 17-7.6 17-17 17zm0-4c7.18 0 13-5.82 13-13s-5.82-13-13-13-13 5.82-13 13 5.82 13 13 13z"/>
<path fill="#6B4FBB" d="M35 45h8c0 2.2-1.8 4-4 4s-4-1.8-4-4zm-1.5-2c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82">
<g fill="none" fill-rule="evenodd">
<path fill="#000" fill-opacity=".03" d="M2.12 42C2.04 43 2 44 2 45c0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3C74.35 61.03 58.42 76 39 76S3.65 61.03 2.12 42z"/>
<path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/>
<path fill="#FEE1D3" fill-rule="nonzero" d="M24.92 35.15c-1.72-1.4-1.98-3.9-.6-5.63l1.26-1.55c1.4-1.72 3.9-2 5.63-.6l.7.56c.7-.4 1.4-.73 2.1-1V26c0-2.2 1.8-4 4-4h2c2.2 0 4 1.8 4 4v.92c.8.28 1.5.62 2.1 1l.7-.55c1.7-1.4 4.3-1.12 5.7.6l1.3 1.55c1.4 1.72 1.2 4.23-.6 5.63l-.7.6c.3.74.4 1.5.5 2.3l.9.2c2.2.5 3.5 2.64 3 4.8L56.4 45c-.5 2.15-2.64 3.5-4.8 3l-.88-.2c-.44.63-.92 1.24-1.46 1.8l.4.82c.9 1.98.1 4.38-1.9 5.35l-1.8.87c-2 .97-4.37.15-5.34-1.84l-.46-.85c-.34.03-.74.05-1.13.05-.4 0-.8-.02-1.2-.05l-.4.85c-.95 2-3.34 2.8-5.33 1.84l-1.8-.87c-1.97-.97-2.8-3.37-1.83-5.35l.4-.8c-.54-.58-1.02-1.2-1.46-1.83l-.8.2c-2.2.5-4.3-.9-4.8-3l-.4-2c-.5-2.2.85-4.3 3-4.8l.9-.2c.1-.8.3-1.6.5-2.3l-.7-.6zm4.95.77c-.53 1.2-.83 2.47-.87 3.8-.02.9-.66 1.68-1.55 1.9l-2.32.53.45 1.94 2.3-.6c.9-.2 1.8.2 2.23 1 .7 1.1 1.5 2.2 2.5 3 .7.6.9 1.6.5 2.4l-1 2.1 1.8.9 1.1-2.1c.4-.8 1.3-1.3 2.2-1.1.7.1 1.3.2 2 .2s1.3-.1 2-.2c.9-.2 1.8.3 2.2 1.1l1 2.1 1.8-.9-1.2-2c-.4-.8-.2-1.8.5-2.4 1-.85 1.84-1.88 2.45-3.05.4-.82 1.33-1.24 2.2-1.04l2.33.54.45-1.95-2.32-.54c-.9-.2-1.52-.97-1.54-1.88-.03-1.4-.33-2.6-.86-3.8-.4-.9-.2-1.8.5-2.4l1.9-1.5-1.3-1.6-1.8 1.5c-.8.5-1.8.6-2.5 0-1.1-.8-2.3-1.4-3.5-1.7-.9-.2-1.5-1-1.5-1.9V26h-2v2.38c0 .9-.6 1.7-1.5 1.93-1.3.4-2.5 1-3.5 1.7-.8.6-1.8.6-2.5 0l-1.9-1.5-1.26 1.6 1.8 1.5c.7.6.94 1.6.6 2.4z"/>
<path fill="#FC6D26" fill-rule="nonzero" d="M39 46c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm0-4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"><g fill="none" fill-rule="evenodd"><path fill="#F9F9F9" d="M2.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3C74.353 61.032 58.425 76 39 76 19.575 76 3.647 61.032 2.12 42z"/><path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/><path fill="#E1DBF2" d="M30.24 27.823A14.98 14.98 0 0 0 24 40c0 2.549.636 4.949 1.757 7.051-.297-2.684.644-4.026 2.823-4.026 3.707 0 2.462 5.365 4.473 5.761 2.01.396 4.175.396 4.267 3.29.04 1.257-.265 2.157-.917 2.7a15.095 15.095 0 0 0 8.555-1.006c.035-1.91.303-4.941 2.21-5.61 2.373-.833-.55-1.431.734-3.368 1.17-1.762-3.297-5.2 0-4.832 3.477.388 5.044-.816 6.024-1.456a14.903 14.903 0 0 0-1.373-4.94c-.873.4-2.19.465-3.702-.538-.757-.502-1.084-3.944-2.107-3.944-3.823 0-4.065 3.17-5.994 3.944-1.076.431-4.193 3.773-5.614 3.596-1.126-.14-1.071-4.417-2.45-5.166-1.359-.738-2.174-1.948-2.447-3.633zM39 59c-10.493 0-19-8.507-19-19s8.507-19 19-19 19 8.507 19 19-8.507 19-19 19z"/></g></svg>
\ No newline at end of file
- show_roles = local_assigns.fetch(:show_roles, true) - show_roles = local_assigns.fetch(:show_roles, true)
- show_controls = local_assigns.fetch(:show_controls, true) - show_controls = local_assigns.fetch(:show_controls, true)
- force_mobile_view = local_assigns.fetch(:force_mobile_view, false)
- user = local_assigns.fetch(:user, member.user) - user = local_assigns.fetch(:user, member.user)
- source = member.source - source = member.source
- can_admin_member = can?(current_user, action_member_permission(:update, member), member) - can_admin_member = can?(current_user, action_member_permission(:update, member), member)
...@@ -8,45 +9,53 @@ ...@@ -8,45 +9,53 @@
%span.list-item-name %span.list-item-name
- if user - if user
= image_tag avatar_icon(user, 40), class: "avatar s40", alt: '' = image_tag avatar_icon(user, 40), class: "avatar s40", alt: ''
%strong .user-info
= link_to user.name, user_path(user) = link_to user.name, user_path(user), class: 'member'
%span.cgray= user.to_reference %span.cgray= user.to_reference
- if user == current_user - if user == current_user
%span.label.label-success.prepend-left-5 It's you %span.label.label-success.prepend-left-5 It's you
- if user.blocked? - if user.blocked?
%label.label.label-danger %label.label.label-danger
%strong Blocked %strong Blocked
- if source.instance_of?(Group) && source != @group - if source.instance_of?(Group) && source != @group
&middot; &middot;
= link_to source.full_name, source, class: "member-group-link" = link_to source.full_name, source, class: "member-group-link"
.hidden-xs.cgray .cgray
- if member.request? - if member.request?
Requested Requested
= time_ago_with_tooltip(member.requested_at) = time_ago_with_tooltip(member.requested_at)
- else - else
Joined #{time_ago_with_tooltip(member.created_at)} Joined #{time_ago_with_tooltip(member.created_at)}
- if member.expires? - if member.expires?
· ·
%span{ class: "#{"text-warning" if member.expires_soon?} has-tooltip", title: member.expires_at.to_time.in_time_zone.to_s(:medium) } %span{ class: "#{"text-warning" if member.expires_soon?} has-tooltip", title: member.expires_at.to_time.in_time_zone.to_s(:medium) }
Expires in #{distance_of_time_in_words_to_now(member.expires_at)} Expires in #{distance_of_time_in_words_to_now(member.expires_at)}
- else - else
= image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: '' = image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: ''
%strong= member.invite_email .user-info
.cgray .member= member.invite_email
Invited .cgray
- if member.created_by Invited
by - if member.created_by
= link_to member.created_by.name, user_path(member.created_by) by
= time_ago_with_tooltip(member.created_at) = link_to member.created_by.name, user_path(member.created_by)
= time_ago_with_tooltip(member.created_at)
- if show_roles - if show_roles
- current_resource = @project || @group - current_resource = @project || @group
.controls.member-controls .controls.member-controls
- if show_controls && member.source == current_resource - if show_controls && member.source == current_resource
- if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source)
= link_to icon('paper-plane'), polymorphic_path([:resend_invite, member]),
method: :post,
class: 'btn btn-default prepend-left-10 hidden-xs',
title: 'Resend invite'
- if user != current_user && can_admin_member - if user != current_user && can_admin_member
= form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f|
= f.hidden_field :access_level = f.hidden_field :access_level
...@@ -75,13 +84,17 @@ ...@@ -75,13 +84,17 @@
- if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source) - if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source)
= link_to 'Resend invite', polymorphic_path([:resend_invite, member]), = link_to 'Resend invite', polymorphic_path([:resend_invite, member]),
method: :post, method: :post,
class: 'btn btn-default prepend-left-10' class: 'btn btn-default prepend-left-10 visible-xs-block'
- elsif member.request? && can_admin_member - elsif member.request? && can_admin_member
= link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]), = link_to polymorphic_path([:approve_access_request, member]),
method: :post, method: :post,
class: 'btn btn-success prepend-left-10', class: 'btn btn-success prepend-left-10',
title: 'Grant access' title: 'Grant access' do
%span{ class: ('visible-xs-block' unless force_mobile_view) }
Grant access
- unless force_mobile_view
= icon('check inverse', class: 'hidden-xs')
- if can?(current_user, action_member_permission(:destroy, member), member) - if can?(current_user, action_member_permission(:destroy, member), member)
- if current_user == user - if current_user == user
...@@ -96,8 +109,9 @@ ...@@ -96,8 +109,9 @@
data: { confirm: remove_member_message(member) }, data: { confirm: remove_member_message(member) },
class: 'btn btn-remove prepend-left-10', class: 'btn btn-remove prepend-left-10',
title: remove_member_title(member) do title: remove_member_title(member) do
%span.visible-xs-block %span{ class: ('visible-xs-block' unless force_mobile_view) }
Delete Delete
= icon('trash', class: 'hidden-xs') - unless force_mobile_view
= icon('trash', class: 'hidden-xs')
- else - else
%span.member-access-text= member.human_access %span.member-access-text= member.human_access
- force_mobile_view = local_assigns.fetch(:force_mobile_view, false)
- if requesters.any? - if requesters.any?
.panel.panel-default.prepend-top-default .panel.panel-default.prepend-top-default{ class: ('panel-mobile' if force_mobile_view ) }
.panel-heading .panel-heading
Users requesting access to Users requesting access to
%strong= membership_source.name %strong= membership_source.name
%span.badge= requesters.size %span.badge= requesters.size
%ul.content-list %ul.content-list.members-list
= render partial: 'shared/members/member', collection: requesters, as: :member = render partial: 'shared/members/member', collection: requesters, as: :member, locals: { force_mobile_view: force_mobile_view }
...@@ -2,18 +2,34 @@ class BackgroundMigrationWorker ...@@ -2,18 +2,34 @@ class BackgroundMigrationWorker
include Sidekiq::Worker include Sidekiq::Worker
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
# Schedules a number of jobs in bulk # Enqueues a number of jobs in bulk.
# #
# The `jobs` argument should be an Array of Arrays, each sub-array must be in # The `jobs` argument should be an Array of Arrays, each sub-array must be in
# the form: # the form:
# #
# [migration-class, [arg1, arg2, ...]] # [migration-class, [arg1, arg2, ...]]
def self.perform_bulk(*jobs) def self.perform_bulk(jobs)
Sidekiq::Client.push_bulk('class' => self, Sidekiq::Client.push_bulk('class' => self,
'queue' => sidekiq_options['queue'], 'queue' => sidekiq_options['queue'],
'args' => jobs) 'args' => jobs)
end end
# Schedules multiple jobs in bulk, with a delay.
#
def self.perform_bulk_in(delay, jobs)
now = Time.now.to_i
schedule = now + delay.to_i
if schedule <= now
raise ArgumentError, 'The schedule time must be in the future!'
end
Sidekiq::Client.push_bulk('class' => self,
'queue' => sidekiq_options['queue'],
'args' => jobs,
'at' => schedule)
end
# Performs the background migration. # Performs the background migration.
# #
# See Gitlab::BackgroundMigration.perform for more information. # See Gitlab::BackgroundMigration.perform for more information.
......
---
title: Update welcome page UX for new users
merge_request: 12662
author:
---
title: Improve members view on mobile
merge_request: 12619
author:
---
title: Fix API Scoping
merge_request: 12300
author:
---
title: Deprecate Healthcheck Access Token in favor of IP whitelist
merge_request:
author:
---
title: Prevent bad data being added to application settings when Redis is unavailable
merge_request: 12750
author:
---
title: Fix crash on /help/ui
merge_request:
author:
---
title: Prevent disabled pagination button to be clicked
merge_request:
author:
---
title: Remove net-ssh gem
merge_request:
author: Takuya Noguchi
---
title: Do not show pipeline schedule button for non-member
merge_request: 12757
author: Takuya Noguchi
---
title: Fix issues with non-UTF8 filenames by always fixing the encoding of tree and
blob paths
merge_request:
author:
---
title: Enable ImportPath in scss-lint
merge_request: 12749
author: Takuya Noguchi
---
title: Enable PropertySpelling in scss-lint
merge_request: 12752
author: Takuya Noguchi
---
title: Enable SpaceAfterComma in scss-lint
merge_request: 12734
author: Takuya Noguchi
---
title: Add creation time filters to user search API for admins
merge_request: 12682
author:
---
title: Improve issue rendering performance with lots of notes from other users
merge_request:
author:
---
title: Fix offline runner detection
merge_request: 11751
author: Alessio Caiazza
---
title: Fixed GFM references not being included when updating issues inline
merge_request:
author:
---
title: Remove CSS for nprogress removed
merge_request: 12737
author: Takuya Noguchi
---
title: Add a simple mode to merge request API
merge_request:
author:
---
title: Remove remaining N+1 queries in merge requests API with emojis and labels
merge_request:
author:
---
title: Toggle import description with import_sources_enabled
merge_request: 12691
author: Brianna Kicia
\ No newline at end of file
---
title: Bump bootsnap to 1.1.1
merge_request: 12425
author: @blackst0ne
...@@ -19,4 +19,132 @@ an ERB file and then loads the resulting YML as its configuration. ...@@ -19,4 +19,132 @@ an ERB file and then loads the resulting YML as its configuration.
This file is called `resque.yml` for historical reasons. We are **NOT** This file is called `resque.yml` for historical reasons. We are **NOT**
using Resque at the moment. It is used to specify Redis configuration using Resque at the moment. It is used to specify Redis configuration
values instead. values when a single database instance of Redis is desired.
# Advanced Redis configuration files
In more advanced configurations of Redis key-value storage, it is desirable
to separate the keys by lifecycle and intended use to ease provisioning and
management of scalable Redis clusters.
These settings provide routing and other configuration data (such as sentinel,
persistence policies, and other Redis customization) for connections
to Redis single instances, Redis sentinel, and Redis clusters.
If desired, the routing URL provided by these settings can be used with:
1. Unix Socket
1. named socket for each Redis instance desired.
2. `database number` for each Redis instance desired.
2. TCP Socket
1. `host name` or IP for each Redis instance desired
2. TCP port number for each Redis instance desired
3. `database number` for each Redis instance desired
## Example URL attribute formats for GitLab Redis `.yml` configuration files
* Unix Socket, default Redis database (0)
* `url: unix:/path/to/redis.sock`
* `url: unix:/path/to/redis.sock?db=`
* Unix Socket, Redis database 44
* `url: unix:/path/to/redis.sock?db=44`
* `url: unix:/path/to/redis.sock?extra=foo&db=44`
* TCP Socket for Redis on localhost, port 6379, database 33
* `url: redis://:mynewpassword@localhost:6379/33`
* TCP Socket for Redis on remote host `myserver`, port 6379, database 33
* `url: redis://:mynewpassword@myserver:6379/33`
## redis.cache.yml
If configured, `redis.cache.yml` overrides the
`resque.yml` settings to configure the Redis database instance
used for `Rails.cache` and other volatile non-persistent data which enhances
the performance of GitLab.
Settings here can be overridden by the environment variable
`GITLAB_REDIS_CACHE_CONFIG_FILE` which provides
an alternate location for configuration settings.
The order of precedence for the URL used to connect to the Redis instance
used for `cache` is:
1. URL from a configuration file pointed to by the
`GITLAB_REDIS_CACHE_CONFIG_FILE` environment variable
2. URL from `redis.cache.yml`
3. URL from a configuration file pointed to by the
`GITLAB_REDIS_CONFIG_FILE` environment variable
4. URL from `resque.yml`
5. `redis://localhost:6380`
The order of precedence for all other configuration settings for `cache`
are selected from only the first of the following files found (if a setting
is not provided in an earlier file, the remainder of the files are not
searched):
1. the configuration file pointed to by the
`GITLAB_REDIS_CACHE_CONFIG_FILE` environment variable
2. the configuration file `redis.cache.yml`
3. the configuration file pointed to by the
`GITLAB_REDIS_CONFIG_FILE` environment variable
4. the configuration file `resque.yml`
## redis.queues.yml
If configured, `redis.queues.yml` overrides the
`resque.yml` settings to configure the Redis database instance
used for clients of `::Gitlab::Redis::Queues`.
These queues are intended to be the foundation
of reliable inter-process communication between modules, whether on the same
host node, or within a cluster. The primary clients of the queues are
SideKiq, Mailroom, CI Runner, Workhorse, and push services. Settings here can
be overridden by the environment variable
`GITLAB_REDIS_QUEUES_CONFIG_FILE` which provides an alternate location for
configuration settings.
The order of precedence for the URL used to connect to the Redis instance
used for `queues` is:
1. URL from a configuration file pointed to by the
`GITLAB_REDIS_QUEUES_CONFIG_FILE` environment variable
2. URL from `redis.queues.yml`
3. URL from a configuration file pointed to by the
`GITLAB_REDIS_CONFIG_FILE` environment variable
4. URL from `resque.yml`
5. `redis://localhost:6381`
The order of precedence for all other configuration settings for `queues`
are selected from only the first of the following files found (if a setting
is not provided in an earlier file, the remainder of the files are not
searched):
1. the configuration file pointed to by the
`GITLAB_REDIS_QUEUES_CONFIG_FILE` environment variable
2. the configuration file `redis.queues.yml`
3. the configuration file pointed to by the
`GITLAB_REDIS_CONFIG_FILE` environment variable
4. the configuration file `resque.yml`
## redis.shared_state.yml
If configured, `redis.shared_state.yml` overrides the
`resque.yml` settings to configure the Redis database instance
used for clients of `::Gitlab::Redis::SharedState` such as session state,
and rate limiting.
Settings here can be overridden by the environment variable
`GITLAB_REDIS_SHARED_STATE_CONFIG_FILE` which provides
an alternate location for configuration settings.
The order of precedence for the URL used to connect to the Redis instance
used for `shared_state` is:
1. URL from a configuration file pointed to by the
`GITLAB_REDIS_SHARED_STATE_CONFIG_FILE` environment variable
2. URL from `redis.shared_state.yml`
3. URL from a configuration file pointed to by the
`GITLAB_REDIS_CONFIG_FILE` environment variable
4. URL from `resque.yml`
5. `redis://localhost:6382`
The order of precedence for all other configuration settings for `shared_state`
are selected from only the first of the following files found (if a setting
is not provided in an earlier file, the remainder of the files are not
searched):
1. the configuration file pointed to by the
`GITLAB_REDIS_SHARED_STATE_CONFIG_FILE` environment variable
2. the configuration file `redis.shared_state.yml`
3. the configuration file pointed to by the
`GITLAB_REDIS_CONFIG_FILE` environment variable
4. the configuration file `resque.yml`
...@@ -6,7 +6,9 @@ Bundler.require(:default, Rails.env) ...@@ -6,7 +6,9 @@ Bundler.require(:default, Rails.env)
module Gitlab module Gitlab
class Application < Rails::Application class Application < Rails::Application
require_dependency Rails.root.join('lib/gitlab/redis') require_dependency Rails.root.join('lib/gitlab/redis/cache')
require_dependency Rails.root.join('lib/gitlab/redis/queues')
require_dependency Rails.root.join('lib/gitlab/redis/shared_state')
require_dependency Rails.root.join('lib/gitlab/request_context') require_dependency Rails.root.join('lib/gitlab/request_context')
# Settings in config/environments/* take precedence over those specified here. # Settings in config/environments/* take precedence over those specified here.
...@@ -26,7 +28,8 @@ module Gitlab ...@@ -26,7 +28,8 @@ module Gitlab
#{config.root}/app/models/members #{config.root}/app/models/members
#{config.root}/app/models/project_services #{config.root}/app/models/project_services
#{config.root}/app/workers/concerns #{config.root}/app/workers/concerns
#{config.root}/app/services/concerns)) #{config.root}/app/services/concerns
#{config.root}/app/finders/concerns))
config.generators.templates.push("#{config.root}/generator_templates") config.generators.templates.push("#{config.root}/generator_templates")
...@@ -141,15 +144,15 @@ module Gitlab ...@@ -141,15 +144,15 @@ module Gitlab
end end
end end
# Use Redis caching across all environments # Use caching across all environments
redis_config_hash = Gitlab::Redis.params caching_config_hash = Gitlab::Redis::Cache.params
redis_config_hash[:namespace] = Gitlab::Redis::CACHE_NAMESPACE caching_config_hash[:namespace] = Gitlab::Redis::Cache::CACHE_NAMESPACE
redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever caching_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
if Sidekiq.server? # threaded context if Sidekiq.server? # threaded context
redis_config_hash[:pool_size] = Sidekiq.options[:concurrency] + 5 caching_config_hash[:pool_size] = Sidekiq.options[:concurrency] + 5
redis_config_hash[:pool_timeout] = 1 caching_config_hash[:pool_timeout] = 1
end end
config.cache_store = :redis_store, redis_config_hash config.cache_store = :redis_store, caching_config_hash
config.active_record.raise_in_transactional_callbacks = true config.active_record.raise_in_transactional_callbacks = true
......
...@@ -5,12 +5,6 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) ...@@ -5,12 +5,6 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
begin
require 'bootsnap/setup'
rescue SystemCallError => exception
$stderr.puts "WARNING: Bootsnap failed to setup: #{exception.message}"
end
# set default directory for multiproces metrics gathering # set default directory for multiproces metrics gathering
if ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test' if ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test'
ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir' ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
......
...@@ -42,3 +42,4 @@ test: &test ...@@ -42,3 +42,4 @@ test: &test
password: password:
# host: localhost # host: localhost
# socket: /tmp/mysql.sock # socket: /tmp/mysql.sock
prepared_statements: false
...@@ -46,3 +46,4 @@ test: &test ...@@ -46,3 +46,4 @@ test: &test
username: postgres username: postgres
password: password:
# host: localhost # host: localhost
prepared_statements: false
...@@ -539,10 +539,15 @@ production: &base ...@@ -539,10 +539,15 @@ production: &base
# enabled: true # enabled: true
# host: localhost # host: localhost
# port: 3808 # port: 3808
prometheus:
## Monitoring
# Built in monitoring settings
monitoring:
# Time between sampling of unicorn socket metrics, in seconds # Time between sampling of unicorn socket metrics, in seconds
# unicorn_sampler_interval: 10 # unicorn_sampler_interval: 10
# IP whitelist to access monitoring endpoints
ip_whitelist:
- 127.0.0.0/8
# #
# 5. Extra customization # 5. Extra customization
......
...@@ -494,10 +494,11 @@ Settings.webpack.dev_server['host'] ||= 'localhost' ...@@ -494,10 +494,11 @@ Settings.webpack.dev_server['host'] ||= 'localhost'
Settings.webpack.dev_server['port'] ||= 3808 Settings.webpack.dev_server['port'] ||= 3808
# #
# Prometheus metrics settings # Monitoring settings
# #
Settings['prometheus'] ||= Settingslogic.new({}) Settings['monitoring'] ||= Settingslogic.new({})
Settings.prometheus['unicorn_sampler_interval'] ||= 10 Settings.monitoring['ip_whitelist'] ||= ['127.0.0.1/8']
Settings.monitoring['unicorn_sampler_interval'] ||= 10
# #
# Testing settings # Testing settings
......
# Make sure we initialize a Redis connection pool before Sidekiq starts # Make sure we initialize a Redis connection pool before multi-threaded
# multi-threaded execution. # execution starts by
Gitlab::Redis.with { nil } # 1. Sidekiq
# 2. Rails.cache
# 3. HTTP clients
Gitlab::Redis::Cache.with { nil }
Gitlab::Redis::Queues.with { nil }
Gitlab::Redis::SharedState.with { nil }
...@@ -119,7 +119,7 @@ def instrument_classes(instrumentation) ...@@ -119,7 +119,7 @@ def instrument_classes(instrumentation)
end end
# rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/AbcSize
Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.prometheus.unicorn_sampler_interval).start Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_sampler_interval).start
Gitlab::Application.configure do |config| Gitlab::Application.configure do |config|
# 0 should be Sentry to catch errors in this middleware # 0 should be Sentry to catch errors in this middleware
......
...@@ -3,4 +3,6 @@ require 'flipper/middleware/memoizer' ...@@ -3,4 +3,6 @@ require 'flipper/middleware/memoizer'
unless Rails.env.test? unless Rails.env.test?
Rails.application.config.middleware.use Flipper::Middleware::Memoizer, Rails.application.config.middleware.use Flipper::Middleware::Memoizer,
lambda { Feature.flipper } lambda { Feature.flipper }
Feature.register_feature_groups
end end
Rails.application.config.peek.adapter = :redis, { client: ::Redis.new(Gitlab::Redis.params) } Rails.application.config.peek.adapter = :redis, { client: ::Redis.new(Gitlab::Redis::Cache.params) }
Peek.into Peek::Views::Host Peek.into Peek::Views::Host
Peek.into Peek::Views::PerformanceBar Peek.into Peek::Views::PerformanceBar
......
...@@ -19,12 +19,12 @@ cookie_key = if Rails.env.development? ...@@ -19,12 +19,12 @@ cookie_key = if Rails.env.development?
if Rails.env.test? if Rails.env.test?
Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session" Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session"
else else
redis_config = Gitlab::Redis.params sessions_config = Gitlab::Redis::SharedState.params
redis_config[:namespace] = Gitlab::Redis::SESSION_NAMESPACE sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE
Gitlab::Application.config.session_store( Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks. :redis_store, # Using the cookie_store would enable session replay attacks.
servers: redis_config, servers: sessions_config,
key: cookie_key, key: cookie_key,
secure: Gitlab.config.gitlab.https, secure: Gitlab.config.gitlab.https,
httponly: true, httponly: true,
......
# Custom Redis configuration # Custom Queues configuration
redis_config_hash = Gitlab::Redis.params queues_config_hash = Gitlab::Redis::Queues.params
redis_config_hash[:namespace] = Gitlab::Redis::SIDEKIQ_NAMESPACE queues_config_hash[:namespace] = Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE
# Default is to retry 25 times with exponential backoff. That's too much. # Default is to retry 25 times with exponential backoff. That's too much.
Sidekiq.default_worker_options = { retry: 3 } Sidekiq.default_worker_options = { retry: 3 }
Sidekiq.configure_server do |config| Sidekiq.configure_server do |config|
config.redis = redis_config_hash config.redis = queues_config_hash
config.server_middleware do |chain| config.server_middleware do |chain|
chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS'] chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS']
...@@ -54,7 +54,7 @@ Sidekiq.configure_server do |config| ...@@ -54,7 +54,7 @@ Sidekiq.configure_server do |config|
end end
Sidekiq.configure_client do |config| Sidekiq.configure_client do |config|
config.redis = redis_config_hash config.redis = queues_config_hash
config.client_middleware do |chain| config.client_middleware do |chain|
chain.add Gitlab::SidekiqStatus::ClientMiddleware chain.add Gitlab::SidekiqStatus::ClientMiddleware
...@@ -74,5 +74,5 @@ begin ...@@ -74,5 +74,5 @@ begin
end end
end end
end end
rescue Redis::BaseError, SocketError, Errno::ENOENT, Errno::EAFNOSUPPORT, Errno::ECONNRESET, Errno::ECONNREFUSED rescue Redis::BaseError, SocketError, Errno::ENOENT, Errno::EADDRNOTAVAIL, Errno::EAFNOSUPPORT, Errno::ECONNRESET, Errno::ECONNREFUSED
end end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment