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
lib/gitlab/diff/position_tracer.rb
app/policies/project_policy.rb
app/models/concerns/relative_positioning.rb
lib/gitlab/redis/*.rb
......@@ -31,6 +31,9 @@ eslint-report.html
/config/initializers/smtp_settings.rb
/config/initializers/relative_url.rb
/config/resque.yml
/config/redis.cache.yml
/config/redis.queues.yml
/config/redis.shared_state.yml
/config/unicorn.rb
/config/secrets.yml
/config/sidekiq.yml
......
......@@ -203,69 +203,69 @@ setup-test-env:
- public/assets
- tmp/tests
rspec-pg 0 20: *rspec-knapsack-pg
rspec-pg 1 20: *rspec-knapsack-pg
rspec-pg 2 20: *rspec-knapsack-pg
rspec-pg 3 20: *rspec-knapsack-pg
rspec-pg 4 20: *rspec-knapsack-pg
rspec-pg 5 20: *rspec-knapsack-pg
rspec-pg 6 20: *rspec-knapsack-pg
rspec-pg 7 20: *rspec-knapsack-pg
rspec-pg 8 20: *rspec-knapsack-pg
rspec-pg 9 20: *rspec-knapsack-pg
rspec-pg 10 20: *rspec-knapsack-pg
rspec-pg 11 20: *rspec-knapsack-pg
rspec-pg 12 20: *rspec-knapsack-pg
rspec-pg 13 20: *rspec-knapsack-pg
rspec-pg 14 20: *rspec-knapsack-pg
rspec-pg 15 20: *rspec-knapsack-pg
rspec-pg 16 20: *rspec-knapsack-pg
rspec-pg 17 20: *rspec-knapsack-pg
rspec-pg 18 20: *rspec-knapsack-pg
rspec-pg 19 20: *rspec-knapsack-pg
rspec-mysql 0 20: *rspec-knapsack-mysql
rspec-mysql 1 20: *rspec-knapsack-mysql
rspec-mysql 2 20: *rspec-knapsack-mysql
rspec-mysql 3 20: *rspec-knapsack-mysql
rspec-mysql 4 20: *rspec-knapsack-mysql
rspec-mysql 5 20: *rspec-knapsack-mysql
rspec-mysql 6 20: *rspec-knapsack-mysql
rspec-mysql 7 20: *rspec-knapsack-mysql
rspec-mysql 8 20: *rspec-knapsack-mysql
rspec-mysql 9 20: *rspec-knapsack-mysql
rspec-mysql 10 20: *rspec-knapsack-mysql
rspec-mysql 11 20: *rspec-knapsack-mysql
rspec-mysql 12 20: *rspec-knapsack-mysql
rspec-mysql 13 20: *rspec-knapsack-mysql
rspec-mysql 14 20: *rspec-knapsack-mysql
rspec-mysql 15 20: *rspec-knapsack-mysql
rspec-mysql 16 20: *rspec-knapsack-mysql
rspec-mysql 17 20: *rspec-knapsack-mysql
rspec-mysql 18 20: *rspec-knapsack-mysql
rspec-mysql 19 20: *rspec-knapsack-mysql
spinach-pg 0 10: *spinach-knapsack-pg
spinach-pg 1 10: *spinach-knapsack-pg
spinach-pg 2 10: *spinach-knapsack-pg
spinach-pg 3 10: *spinach-knapsack-pg
spinach-pg 4 10: *spinach-knapsack-pg
spinach-pg 5 10: *spinach-knapsack-pg
spinach-pg 6 10: *spinach-knapsack-pg
spinach-pg 7 10: *spinach-knapsack-pg
spinach-pg 8 10: *spinach-knapsack-pg
spinach-pg 9 10: *spinach-knapsack-pg
spinach-mysql 0 10: *spinach-knapsack-mysql
spinach-mysql 1 10: *spinach-knapsack-mysql
spinach-mysql 2 10: *spinach-knapsack-mysql
spinach-mysql 3 10: *spinach-knapsack-mysql
spinach-mysql 4 10: *spinach-knapsack-mysql
spinach-mysql 5 10: *spinach-knapsack-mysql
spinach-mysql 6 10: *spinach-knapsack-mysql
spinach-mysql 7 10: *spinach-knapsack-mysql
spinach-mysql 8 10: *spinach-knapsack-mysql
spinach-mysql 9 10: *spinach-knapsack-mysql
rspec-pg 0 25: *rspec-knapsack-pg
rspec-pg 1 25: *rspec-knapsack-pg
rspec-pg 2 25: *rspec-knapsack-pg
rspec-pg 3 25: *rspec-knapsack-pg
rspec-pg 4 25: *rspec-knapsack-pg
rspec-pg 5 25: *rspec-knapsack-pg
rspec-pg 6 25: *rspec-knapsack-pg
rspec-pg 7 25: *rspec-knapsack-pg
rspec-pg 8 25: *rspec-knapsack-pg
rspec-pg 9 25: *rspec-knapsack-pg
rspec-pg 10 25: *rspec-knapsack-pg
rspec-pg 11 25: *rspec-knapsack-pg
rspec-pg 12 25: *rspec-knapsack-pg
rspec-pg 13 25: *rspec-knapsack-pg
rspec-pg 14 25: *rspec-knapsack-pg
rspec-pg 15 25: *rspec-knapsack-pg
rspec-pg 16 25: *rspec-knapsack-pg
rspec-pg 17 25: *rspec-knapsack-pg
rspec-pg 18 25: *rspec-knapsack-pg
rspec-pg 19 25: *rspec-knapsack-pg
rspec-pg 20 25: *rspec-knapsack-pg
rspec-pg 21 25: *rspec-knapsack-pg
rspec-pg 22 25: *rspec-knapsack-pg
rspec-pg 23 25: *rspec-knapsack-pg
rspec-pg 24 25: *rspec-knapsack-pg
rspec-mysql 0 25: *rspec-knapsack-mysql
rspec-mysql 1 25: *rspec-knapsack-mysql
rspec-mysql 2 25: *rspec-knapsack-mysql
rspec-mysql 3 25: *rspec-knapsack-mysql
rspec-mysql 4 25: *rspec-knapsack-mysql
rspec-mysql 5 25: *rspec-knapsack-mysql
rspec-mysql 6 25: *rspec-knapsack-mysql
rspec-mysql 7 25: *rspec-knapsack-mysql
rspec-mysql 8 25: *rspec-knapsack-mysql
rspec-mysql 9 25: *rspec-knapsack-mysql
rspec-mysql 10 25: *rspec-knapsack-mysql
rspec-mysql 11 25: *rspec-knapsack-mysql
rspec-mysql 12 25: *rspec-knapsack-mysql
rspec-mysql 13 25: *rspec-knapsack-mysql
rspec-mysql 14 25: *rspec-knapsack-mysql
rspec-mysql 15 25: *rspec-knapsack-mysql
rspec-mysql 16 25: *rspec-knapsack-mysql
rspec-mysql 17 25: *rspec-knapsack-mysql
rspec-mysql 18 25: *rspec-knapsack-mysql
rspec-mysql 19 25: *rspec-knapsack-mysql
rspec-mysql 20 25: *rspec-knapsack-mysql
rspec-mysql 21 25: *rspec-knapsack-mysql
rspec-mysql 22 25: *rspec-knapsack-mysql
rspec-mysql 23 25: *rspec-knapsack-mysql
rspec-mysql 24 25: *rspec-knapsack-mysql
spinach-pg 0 5: *spinach-knapsack-pg
spinach-pg 1 5: *spinach-knapsack-pg
spinach-pg 2 5: *spinach-knapsack-pg
spinach-pg 3 5: *spinach-knapsack-pg
spinach-pg 4 5: *spinach-knapsack-pg
spinach-mysql 0 5: *spinach-knapsack-mysql
spinach-mysql 1 5: *spinach-knapsack-mysql
spinach-mysql 2 5: *spinach-knapsack-mysql
spinach-mysql 3 5: *spinach-knapsack-mysql
spinach-mysql 4 5: *spinach-knapsack-mysql
# Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis
......@@ -557,3 +557,4 @@ gitlab_git_test:
SETUP_DB: "false"
script:
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
<<: *except-docs
......@@ -93,7 +93,7 @@ linters:
# The basenames of @imported SCSS partials should not begin with an
# underscore and should not include the filename extension.
ImportPath:
enabled: false
enabled: true
# Avoid using !important in properties. It is usually indicative of a
# misunderstanding of CSS specificity and can lead to brittle code.
......@@ -133,7 +133,7 @@ linters:
# Reports when you use an unknown or disabled CSS property
# (ignoring vendor-prefixed properties).
PropertySpelling:
enabled: false
enabled: true
# Configure which units are allowed for property values.
PropertyUnits:
......@@ -176,7 +176,7 @@ linters:
# Commas in lists should be followed by a space.
SpaceAfterComma:
enabled: false
enabled: true
# Properties should be formatted with a single space separating the colon
# from the property's value.
......
......@@ -2,6 +2,13 @@
documentation](doc/development/changelog.md) for instructions on adding your own
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)
- 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'
gem 'rails', '4.2.8'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
gem 'bootsnap', '~> 1.1'
# Responders respond_to and respond_with
gem 'responders', '~> 2.0'
......@@ -251,7 +250,6 @@ gem 'jquery-rails', '~> 4.1.0'
gem 'request_store', '~> 1.3'
gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1'
gem 'net-ssh', '~> 3.0.1'
gem 'base32', '~> 0.3.0'
# Sentry integration
......
......@@ -83,8 +83,6 @@ GEM
bindata (2.3.5)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
bootsnap (1.1.1)
msgpack (~> 1.0)
bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4)
......@@ -465,7 +463,6 @@ GEM
minitest (5.7.0)
mmap2 (2.2.7)
mousetrap-rails (1.4.6)
msgpack (1.1.0)
multi_json (1.12.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
......@@ -475,7 +472,6 @@ GEM
mustermann (= 0.4.0)
mysql2 (0.3.20)
net-ldap (0.12.1)
net-ssh (3.0.1)
netrc (0.11.0)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
......@@ -783,7 +779,7 @@ GEM
rack
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
sidekiq (5.0.0)
sidekiq (5.0.4)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0)
......@@ -930,7 +926,6 @@ DEPENDENCIES
benchmark-ips (~> 2.3.0)
better_errors (~> 2.1.0)
binding_of_caller (~> 0.7.2)
bootsnap (~> 1.1)
bootstrap-sass (~> 3.3.0)
bootstrap_form (~> 2.7.0)
brakeman (~> 3.6.0)
......@@ -1017,7 +1012,6 @@ DEPENDENCIES
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16)
net-ssh (~> 3.0.1)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.4)
octokit (~> 4.6.2)
......
......@@ -46,6 +46,8 @@ export default {
},
methods: {
changePage(e) {
if (e.target.parentElement.classList.contains('disabled')) return;
const text = e.target.innerText;
const { totalPages, nextPage, previousPage } = this.pageInfo;
......@@ -82,7 +84,9 @@ export default {
const page = this.pageInfo.page;
const items = [];
if (page > 1) items.push({ title: FIRST });
if (page > 1) {
items.push({ title: FIRST, first: true });
}
if (page > 1) {
items.push({ title: PREV, prev: true });
......@@ -110,7 +114,9 @@ export default {
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;
},
......@@ -124,13 +130,15 @@ export default {
v-for="item in getItems"
:class="{
page: item.page,
prev: item.prev,
next: item.next,
'js-previous-button': item.prev,
'js-next-button': item.next,
'js-last-button': item.last,
'js-first-button': item.first,
separator: item.separator,
active: item.active,
disabled: item.disabled
}">
<a @click="changePage($event)">{{item.title}}</a>
<a @click.prevent="changePage($event)">{{item.title}}</a>
</li>
</ul>
</div>
......
......@@ -42,4 +42,4 @@
/*
* Styles for JS behaviors.
*/
@import "behaviors.scss";
@import "behaviors";
......@@ -4,49 +4,49 @@
@import 'framework/tw_bootstrap';
@import "framework/layout";
@import "framework/animations.scss";
@import "framework/avatar.scss";
@import "framework/asciidoctor.scss";
@import "framework/blocks.scss";
@import "framework/buttons.scss";
@import "framework/badges.scss";
@import "framework/calendar.scss";
@import "framework/callout.scss";
@import "framework/common.scss";
@import "framework/dropdowns.scss";
@import "framework/files.scss";
@import "framework/filters.scss";
@import "framework/flash.scss";
@import "framework/forms.scss";
@import "framework/gfm.scss";
@import "framework/header.scss";
@import "framework/highlight.scss";
@import "framework/issue_box.scss";
@import "framework/jquery.scss";
@import "framework/lists.scss";
@import "framework/logo.scss";
@import "framework/markdown_area.scss";
@import "framework/mobile.scss";
@import "framework/modal.scss";
@import "framework/nav.scss";
@import "framework/pagination.scss";
@import "framework/panels.scss";
@import "framework/selects.scss";
@import "framework/sidebar.scss";
@import "framework/tables.scss";
@import "framework/notes.scss";
@import "framework/timeline.scss";
@import "framework/typography.scss";
@import "framework/zen.scss";
@import "framework/animations";
@import "framework/avatar";
@import "framework/asciidoctor";
@import "framework/blocks";
@import "framework/buttons";
@import "framework/badges";
@import "framework/calendar";
@import "framework/callout";
@import "framework/common";
@import "framework/dropdowns";
@import "framework/files";
@import "framework/filters";
@import "framework/flash";
@import "framework/forms";
@import "framework/gfm";
@import "framework/header";
@import "framework/highlight";
@import "framework/issue_box";
@import "framework/jquery";
@import "framework/lists";
@import "framework/logo";
@import "framework/markdown_area";
@import "framework/mobile";
@import "framework/modal";
@import "framework/nav";
@import "framework/pagination";
@import "framework/panels";
@import "framework/selects";
@import "framework/sidebar";
@import "framework/tables";
@import "framework/notes";
@import "framework/timeline";
@import "framework/typography";
@import "framework/zen";
@import "framework/blank";
@import "framework/wells.scss";
@import "framework/page-header.scss";
@import "framework/awards.scss";
@import "framework/images.scss";
@import "framework/wells";
@import "framework/page-header";
@import "framework/awards";
@import "framework/images";
@import "framework/broadcast-messages";
@import "framework/emojis.scss";
@import "framework/emoji-sprites.scss";
@import "framework/icons.scss";
@import "framework/snippets.scss";
@import "framework/memory_graph.scss";
@import "framework/responsive-tables.scss";
@import "framework/emojis";
@import "framework/emoji-sprites";
@import "framework/icons";
@import "framework/snippets";
@import "framework/memory_graph";
@import "framework/responsive-tables";
......@@ -24,7 +24,7 @@
opacity: 0;
transform: scale(.2);
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;
&.is-aligned-right {
......
.blank-state-welcome {
text-align: center;
border-bottom: 1px solid $border-color;
.blank-state-parent-container {
.section-container {
padding: 10px;
}
.blank-state-text {
margin-bottom: 0;
.section-body {
width: 100%;
height: 100%;
padding-bottom: 25px;
border: 1px solid $border-color;
border-radius: $border-radius-default;
}
}
......@@ -11,41 +16,35 @@
padding-top: 20px;
padding-bottom: 20px;
text-align: center;
}
.blank-state-no-icon {
padding-top: 40px;
padding-bottom: 40px;
}
.blank-state-icon {
padding-bottom: 20px;
color: $gray-darkest;
font-size: 56px;
&.blank-state-welcome {
.blank-state-welcome-title {
font-size: 24px;
}
path,
polygon {
fill: currentColor;
.blank-state-text {
margin-bottom: 0;
}
}
}
.blank-state-title {
margin-top: 0;
margin-bottom: 5px;
font-size: 18px;
font-weight: normal;
}
.blank-state-icon {
padding-bottom: 20px;
.blank-state-text {
margin-top: 0;
margin-bottom: $gl-padding;
font-size: 14px;
svg {
display: block;
margin: auto;
}
}
> strong {
font-weight: 600;
.blank-state-title {
margin-top: 0;
margin-bottom: 10px;
font-size: 18px;
}
}
.blank-state-welcome-title {
font-size: 24px;
.blank-state-text {
max-width: $container-text-max-width;
margin: 0 auto $gl-padding;
font-size: 14px;
}
}
......@@ -336,11 +336,6 @@ table {
text-align: center;
}
#nprogress .spinner {
top: 15px !important;
right: 10px !important;
}
.header-with-avatar {
h3 {
margin: 0;
......@@ -450,4 +445,3 @@ table {
pointer-events: none;
opacity: .5;
}
......@@ -343,6 +343,12 @@ ul.indent-list {
.group-row {
padding: 0;
border: none;
&:last-of-type {
.group-row-contents:not(:hover) {
border-bottom: 1px solid transparent;
}
}
}
.group-row-contents {
......
......@@ -74,11 +74,17 @@ $red-700: #a62d19;
$red-800: #8b2615;
$red-900: #711e11;
$purple-600: #6e49cb;
$purple-650: #5c35ae;
$purple-700: #4a2192;
$purple-800: #2c0a5c;
$purple-900: #380d75;
$indigo-50: #f7f7ff;
$indigo-100: #ebebfa;
$indigo-200: #d1d1f0;
$indigo-300: #a6a6de;
$indigo-400: #7c7ccc;
$indigo-500: #6666c4;
$indigo-600: #5b5bbd;
$indigo-700: #4b4ba3;
$indigo-800: #393982;
$indigo-900: #292961;
$indigo-950: #1a1a40;
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
......@@ -170,6 +176,7 @@ $header-height: 50px;
$fixed-layout-width: 1280px;
$limited-layout-width: 990px;
$limited-layout-width-sm: 790px;
$container-text-max-width: 540px;
$gl-avatar-size: 40px;
$error-exclamation-point: $red-500;
$border-radius-default: 3px;
......@@ -310,7 +317,7 @@ $badge-color: $gl-text-color-secondary;
/*
* 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-lines: #bb9c13;
......@@ -561,7 +568,7 @@ $kdb-color: #555;
$kdb-border: #ccc;
$kdb-border-bottom: #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
......
......@@ -113,7 +113,7 @@ $white-gc-bg: #eaf2f5;
border-color: $line-removed-dark;
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;
border-color: $line-added-dark;
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;
background-color: $line-removed;
&::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 {
......@@ -175,7 +175,7 @@ $white-gc-bg: #eaf2f5;
background-color: $line-added;
&::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 {
......
......@@ -4,7 +4,7 @@
header.navbar-gitlab-new {
color: $white-light;
background-color: $purple-900;
background: linear-gradient(to right, $indigo-900, $indigo-800);
border-bottom: 0;
.header-content {
......@@ -24,11 +24,9 @@ header.navbar-gitlab-new {
> a {
display: flex;
align-items: center;
padding-top: 3px;
padding-right: $gl-padding;
padding-left: $gl-padding;
margin-left: -$gl-padding;
border-bottom: 3px solid transparent;
@media (min-width: $screen-sm-min) {
padding-right: $gl-padding;
......@@ -45,9 +43,8 @@ header.navbar-gitlab-new {
&:hover,
&:focus {
color: currentColor;
color: $tanuki-yellow;
text-decoration: none;
border-bottom-color: $white-light;
}
}
}
......@@ -71,7 +68,7 @@ header.navbar-gitlab-new {
.navbar-collapse {
padding-left: 0;
color: $white-light;
color: $indigo-200;
box-shadow: 0;
@media (max-width: $screen-xs-max) {
......@@ -101,7 +98,7 @@ header.navbar-gitlab-new {
font-size: 14px;
text-align: center;
color: currentColor;
border-left: 1px solid lighten($purple-700, 10%);
border-left: 1px solid lighten($indigo-700, 10%);
&:hover,
&:focus,
......@@ -120,6 +117,7 @@ header.navbar-gitlab-new {
li {
.badge {
box-shadow: none;
font-weight: 600;
}
}
}
......@@ -133,12 +131,11 @@ header.navbar-gitlab-new {
> a {
background: none;
opacity: .9;
will-change: opacity;
will-change: color;
&.header-user-dropdown-toggle {
.header-user-avatar {
border-color: $white-light;
border-color: $indigo-200;
}
}
......@@ -165,29 +162,34 @@ header.navbar-gitlab-new {
.navbar-sub-nav {
display: flex;
margin-bottom: 0;
color: $white-light;
color: $indigo-200;
> li {
&.active > a,
a:hover,
a:focus {
border-bottom-color: $white-light;
> a:hover,
> a:focus {
box-shadow: inset 0 -3px 0 rgba($indigo-200, .4);
text-decoration: none;
outline: 0;
opacity: 1;
color: $white-light;
}
&.active > a {
box-shadow: inset 0 -3px 0 $indigo-500;
color: $white-light;
font-weight: 700;
}
> a {
display: block;
padding: 16px 10px 13px;
padding: 16px 10px;
font-size: 13px;
color: currentColor;
border-bottom: 3px solid transparent;
opacity: .9;
will-change: opacity;
box-shadow: inset 0 0 0 transparent;
will-change: box-shadow;
transition: box-shadow 0.15s;
@media (min-width: $screen-sm-min) {
padding: 15px $gl-padding 12px;
padding: 15px $gl-padding;
font-size: 14px;
}
}
......@@ -207,55 +209,60 @@ header.navbar-gitlab-new {
.search {
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 {
border-color: rgba($white-light, .6);
background-color: rgba($indigo-200, .3);
box-shadow: none;
}
}
&.search-active form {
border-color: $white-light;
}
form,
.search-input {
background-color: $purple-700;
background-color: rgba($indigo-200, .3);
box-shadow: none;
}
.search-input {
color: $white-light;
background: none;
}
.search-input::placeholder {
color: rgba($white-light, .6);
color: rgba($indigo-200, .8);
}
.location-badge {
font-size: 12px;
color: rgba($white-light, .6);
background-color: $purple-800;
color: $indigo-100;
background-color: rgba($indigo-200, .1);
transition: color 0.15s;
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-icon,
.clear-icon {
color: rgba($white-light, .6);
color: rgba($indigo-200, .8);
}
}
&.search-active {
.location-badge {
color: $white-light;
background-color: $purple-800;
background-color: rgba($indigo-200, .2);
}
.search-input-wrap {
.search-icon {
color: rgba($white-light, .6);
color: rgba($indigo-200, .8);
}
.clear-icon {
......
......@@ -2,6 +2,15 @@
@import 'framework/tw_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;
.page-with-new-sidebar {
......@@ -17,19 +26,33 @@ $new-sidebar-width: 220px;
}
.context-header {
background-color: $gray-normal;
border-bottom: 1px solid $border-color;
font-weight: 600;
display: flex;
align-items: center;
padding: 10px 14px 9px;
padding: 10px 16px 10px;
color: $gl-text-color;
.avatar-container {
flex: 0 0 40px;
}
&: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,
......@@ -41,6 +64,7 @@ $new-sidebar-width: 220px;
.settings-avatar {
background-color: $white-light;
transition: background-color 100ms linear;
i {
font-size: 20px;
......@@ -48,6 +72,7 @@ $new-sidebar-width: 220px;
color: $gl-text-color-secondary;
text-align: center;
align-self: center;
transition: color 100ms linear;
}
}
......@@ -60,11 +85,15 @@ $new-sidebar-width: 220px;
bottom: 0;
left: 0;
overflow: auto;
background-color: $gray-light;
border-right: 1px solid $border-color;
background-color: $gray-normal;
box-shadow: inset -2px 0 0 $border-color;
a {
text-decoration: none;
}
ul {
padding: 0;
padding-left: 0;
list-style: none;
}
......@@ -73,13 +102,18 @@ $new-sidebar-width: 220px;
a {
display: block;
padding: 12px 14px;
padding: 12px 16px;
color: $inactive-color;
}
}
a {
color: $gl-text-color;
text-decoration: none;
li.active {
box-shadow: inset 4px 0 0 $active-border;
> a {
color: $active-color;
font-weight: 700;
}
}
@media (max-width: $screen-xs-max) {
......@@ -89,22 +123,28 @@ $new-sidebar-width: 220px;
.sidebar-sub-level-items {
display: none;
padding-bottom: 8px;
> li {
a {
padding: 12px 24px;
color: $gl-text-color-light;
font-size: 12px;
padding: 8px 16px 8px 24px;
&:hover {
color: $gl-text-color;
background-color: $border-color;
&:hover,
&:focus {
background: $active-hover-background;
color: $active-hover-color;
}
}
&.active {
> a {
color: $purple-650;
font-weight: 600;
a {
&,
&:hover,
&:focus {
background: $active-background;
color: $active-color;
}
}
}
}
......@@ -114,35 +154,31 @@ $new-sidebar-width: 220px;
> li {
.badge {
float: right;
background-color: $border-color;
color: $gl-text-color;
background-color: $inactive-badge-background;
color: $inactive-color;
}
&.active {
> a {
background-color: $purple-600;
color: $white-light;
font-weight: 600;
}
background: $active-background;
.badge {
background-color: $purple-700;
color: $white-light;
color: $active-color;
font-weight: 600;
}
.sidebar-sub-level-items {
background-color: $gray-normal;
border-left: 6px solid $purple-600;
display: block;
}
}
&:not(.active) > a:hover {
background-color: $border-color;
> a:hover {
background-color: $hover-background;
color: $hover-color;
.badge {
transition: background-color 100ms linear;
background-color: $gray-normal;
transition: background-color 100ms linear, color 100ms linear;
background-color: $indigo-500;
color: $hover-color;
}
}
}
......@@ -161,3 +197,13 @@ $new-sidebar-width: 220px;
// 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 @@
@keyframes blinking-dots {
0% {
background-color: rgba($white-light, 1);
box-shadow: 12px 0 0 0 rgba($white-light,0.2),
24px 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);
}
25% {
background-color: rgba($white-light, 0.4);
box-shadow: 12px 0 0 0 rgba($white-light,2),
24px 0 0 0 rgba($white-light,0.2);
box-shadow: 12px 0 0 0 rgba($white-light, 2),
24px 0 0 0 rgba($white-light, 0.2);
}
75% {
background-color: rgba($white-light, 0.4);
box-shadow: 12px 0 0 0 rgba($white-light,0.2),
24px 0 0 0 rgba($white-light,1);
box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
24px 0 0 0 rgba($white-light, 1);
}
100% {
background-color: rgba($white-light, 1);
box-shadow: 12px 0 0 0 rgba($white-light,0.2),
24px 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);
}
}
......
......@@ -54,8 +54,6 @@
@media (min-width: $screen-sm-min) {
display: -webkit-flex;
display: flex;
width: 400px;
max-width: 50%;
}
}
......@@ -65,7 +63,6 @@
@media (min-width: $screen-sm-min) {
display: -webkit-flex;
display: flex;
width: 100%;
margin-top: 3px;
}
}
......@@ -81,18 +78,10 @@
.member-form-control {
@media (max-width: $screen-xs-max) {
padding: 5px 0;
padding-bottom: 5px;
margin-left: 0;
margin-right: 0;
}
@media (min-width: $screen-sm-min) {
width: 50%;
}
.dropdown-menu-toggle {
width: 100%;
}
}
.member-access-text {
......@@ -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 @@
.dropdown-menu {
margin-top: 11px;
z-index: 200;
z-index: 300;
}
.ci-action-icon-wrapper {
......
......@@ -286,8 +286,7 @@ table.u2f-registrations {
}
.user-callout {
margin: 0 auto;
max-width: $screen-lg-min;
margin: 20px -5px 0;
.bordered-box {
border: 1px solid $blue-300;
......
module IssuableCollections
extend ActiveSupport::Concern
include SortingHelper
include Gitlab::IssuableMetadata
included do
helper_method :issues_finder
......@@ -9,39 +10,6 @@ module IssuableCollections
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
issues_finder.execute.preload(:project, :author, :assignees, :labels, :milestone, project: :namespace)
end
......
module RequiresHealthToken
module RequiresWhitelistedMonitoringClient
extend ActiveSupport::Concern
included do
before_action :validate_health_check_access!
before_action :validate_ip_whitelisted_or_valid_token!
end
private
def validate_health_check_access!
render_404 unless token_valid?
def validate_ip_whitelisted_or_valid_token!
render_404 unless client_ip_whitelisted? || valid_token?
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.present? &&
ActiveSupport::SecurityUtils.variable_size_secure_compare(
......
class HealthCheckController < HealthCheck::HealthCheckController
include RequiresHealthToken
include RequiresWhitelistedMonitoringClient
end
class HealthController < ActionController::Base
protect_from_forgery with: :exception
include RequiresHealthToken
include RequiresWhitelistedMonitoringClient
CHECKS = [
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
].freeze
......
class MetricsController < ActionController::Base
include RequiresHealthToken
include RequiresWhitelistedMonitoringClient
protect_from_forgery with: :exception
before_action :validate_prometheus_metrics
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
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 @@
# iids: integer[]
#
class IssuableFinder
include CreatedAtFilter
NONE = '0'.freeze
IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze
......@@ -32,6 +34,7 @@ class IssuableFinder
def execute
items = init_collection
items = by_scope(items)
items = by_created_at(items)
items = by_state(items)
items = by_group(items)
items = by_search(items)
......@@ -42,7 +45,6 @@ class IssuableFinder
items = by_iids(items)
items = by_milestone(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
items = by_project(items)
......@@ -411,18 +413,6 @@ class IssuableFinder
params[:non_archived].present? ? items.non_archived : items
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?
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end
......
......@@ -49,7 +49,8 @@ class MilestonesFinder
if params.has_key?(:order)
items.reorder(params[:order])
else
items.reorder('due_date ASC')
order_statement = Gitlab::Database.nulls_last_order('due_date', 'ASC')
items.reorder(order_statement)
end
end
end
......@@ -14,6 +14,8 @@
# external: boolean
#
class UsersFinder
include CreatedAtFilter
attr_accessor :current_user, :params
def initialize(current_user, params = {})
......@@ -29,6 +31,7 @@ class UsersFinder
users = by_active(users)
users = by_external_identity(users)
users = by_external(users)
users = by_created_at(users)
users
end
......
......@@ -2,6 +2,10 @@
module GitlabRoutingHelper
extend ActiveSupport::Concern
included do
Gitlab::Routing.includes_helpers(self)
end
# Project
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
......
......@@ -184,6 +184,9 @@ class ApplicationSetting < ActiveRecord::Base
Rails.cache.fetch(CACHE_KEY) do
ApplicationSetting.last
end
rescue
# Fall back to an uncached value if there are any problems (e.g. redis down)
ApplicationSetting.last
end
def self.expire
......
......@@ -3,7 +3,7 @@ module Ci
extend Ci::Model
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
FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze
......@@ -19,7 +19,7 @@ module Ci
scope :shared, ->() { where(is_shared: true) }
scope :active, ->() { where(active: true) }
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 :owned_or_shared, ->(project_id) do
......@@ -59,6 +59,10 @@ module Ci
where(t[:token].matches(pattern).or(t[:description].matches(pattern)))
end
def self.contact_time_deadline
ONLINE_CONTACT_TIMEOUT.ago
end
def set_default_values
self.token = SecureRandom.hex(15) if self.token.blank?
end
......@@ -80,7 +84,7 @@ module Ci
end
def online?
contacted_at && contacted_at > LAST_CONTACT_TIME
contacted_at && contacted_at > self.class.contact_time_deadline
end
def status
......@@ -145,7 +149,7 @@ module Ci
private
def cleanup_runner_queue
Gitlab::Redis.with do |redis|
Gitlab::Redis::Queues.with do |redis|
redis.del(runner_queue_key)
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
include FasterCacheKeys
include RelativePositioning
include IgnorableColumn
include CreatedAtFilterable
ignore_column :position
......@@ -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_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) }
after_save :expire_etag_cache
......
......@@ -5,6 +5,7 @@ class MergeRequest < ActiveRecord::Base
include Referable
include Sortable
include IgnorableColumn
include CreatedAtFilterable
ignore_column :position
......@@ -14,7 +15,7 @@ class MergeRequest < ActiveRecord::Base
has_many :merge_request_diffs
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"
......
......@@ -536,6 +536,11 @@ class Project < ActiveRecord::Base
ProjectCacheWorker.perform_async(self.id)
end
remove_import_data
end
# This method is overriden in EE::Project model
def remove_import_data
import_data&.destroy
end
......@@ -796,10 +801,12 @@ class Project < ActiveRecord::Base
update_column(:has_external_wiki, services.external_wikis.any?)
end
def find_or_initialize_services
def find_or_initialize_services(exceptions: [])
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)
if service
......@@ -1379,15 +1386,15 @@ class Project < ActiveRecord::Base
end
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
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
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
def route_map_for(commit_sha)
......@@ -1450,7 +1457,7 @@ class Project < ActiveRecord::Base
from && self != from
end
def pushes_since_gc_redis_key
def pushes_since_gc_redis_shared_state_key
"projects/#{id}/pushes_since_gc"
end
......
......@@ -12,6 +12,7 @@ class User < ActiveRecord::Base
include TokenAuthenticatable
include IgnorableColumn
include FeatureGate
include CreatedAtFilterable
DEFAULT_NOTIFICATION_LEVEL = :participating
......
......@@ -3,7 +3,10 @@ require 'prometheus/client/formats/text'
class MetricsService
CHECKS = [
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
].freeze
......
module Milestones
class DestroyService < Milestones::BaseService
def execute(milestone)
return unless milestone.is_project_milestone?
Milestone.transaction do
update_params = { milestone: nil }
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
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
event_service.destroy_milestone(milestone, current_user)
......
......@@ -107,8 +107,7 @@
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
= button_tag 'Add users to group', class: "btn btn-create"
= render 'shared/members/requests', membership_source: @group, requesters: @requesters
= render 'shared/members/requests', membership_source: @group, requesters: @requesters, force_mobile_view: true
.panel.panel-default
.panel-heading
......@@ -117,7 +116,7 @@
%span.badge= @group.members.size
.pull-right
= 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 }
.panel-footer
= paginate @members, param_name: 'members_page', theme: 'gitlab'
......@@ -160,12 +160,12 @@
.pull-right
= link_to admin_group_path(@group), class: 'btn btn-xs' do
= 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 }
.panel-footer
= 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-heading
......@@ -174,7 +174,7 @@
%span.badge= @project.users.size
.pull-right
= 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 }
.panel-footer
= 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
.blank-state.blank-state-welcome
%h2.blank-state-welcome-title
Welcome to GitLab
%p.blank-state-text
Code, test, and deploy together
- if current_user.can_create_group?
.blank-state
.blank-state-icon
= custom_icon("group", size: 50)
%h3.blank-state-title
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
.row.blank-state-parent-container
.section-container.section-welcome{ class: "#{ 'section-admin-welcome' if current_user.admin? }" }
.container.section-body
.blank-state.blank-state-welcome
%h2.blank-state-welcome-title
Welcome to GitLab
%p.blank-state-text
Code, test, and deploy together
- if current_user.admin?
= render "blank_state_admin_welcome"
- else
= render "blank_state_welcome"
......@@ -28,6 +28,6 @@
Members with access to
%strong= @group.name
%span.badge= @members.total_count
%ul.content-list
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member
= paginate @members, theme: 'gitlab'
......@@ -525,7 +525,7 @@
%h4
%code .file-holder
- blob = Snippet.new(content: "Wow\nSuch\nFile")
- blob = Snippet.new(content: "Wow\nSuch\nFile").blob
.example
.file-holder
.js-file-title.file-title
......
......@@ -3,7 +3,7 @@
- hide_top_links = @hide_top_links || false
%nav.breadcrumbs{ role: "navigation" }
.breadcrumbs-container{ class: container_class }
.breadcrumbs-container{ class: [container_class, @content_class] }
.breadcrumbs-links.js-title-container
- unless hide_top_links
.title
......
......@@ -74,9 +74,9 @@
= 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
%span
Issues
- if @project.default_issues_tracker?
%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
- if project_nav_tab?(:issues) && !current_controller?(:merge_requests)
......@@ -112,8 +112,8 @@
= 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
%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)
Merge Requests
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
......
- @no_container = true
- container_class = !fluid_layout && diff_view == :inline ? 'container-limited' : ''
- limited_container_width = fluid_layout ? '' : 'limit-container-width'
- @content_class = limited_container_width
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
- page_description @commit.description
= render "projects/commits/head"
......
......@@ -11,8 +11,9 @@
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
New project
%p
Create or Import your project from popular Git services
- if import_sources_enabled?
%p
Create or Import your project from popular Git services
.col-lg-9
= form_for @project, html: { class: 'new_project' } do |f|
.row
......
......@@ -7,7 +7,7 @@
- @no_container = true
- page_title _("Pipeline Schedules")
- if show_new_nav?
- if show_new_nav? && can?(current_user, :create_pipeline_schedule, @project)
- content_for :breadcrumbs_extra do
= link_to _('New schedule'), new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create'
......@@ -21,9 +21,10 @@
- schedule_path_proc = ->(scope) { pipeline_schedules_path(@project, 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?) }
= link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do
%span= _('New schedule')
- if can?(current_user, :create_pipeline_schedule, @project)
.nav-controls{ class: ("visible-xs" if show_new_nav?) }
= link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do
%span= _('New schedule')
- if @schedules.present?
%ul.content-list
......
......@@ -11,5 +11,5 @@
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
= render 'shared/members/sort_dropdown'
%ul.content-list
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: members, as: :member
- @no_container = true
- @breadcrumb_title = "Project"
- @content_class = "limit-container-width" unless fluid_layout
- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message
= content_for :meta_tags do
......
- @no_container = true
- @breadcrumb_title = _("Repository")
- @content_class = "limit-container-width" unless fluid_layout
- page_title @path.presence || _("Files"), @ref
= 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_controls = local_assigns.fetch(:show_controls, true)
- force_mobile_view = local_assigns.fetch(:force_mobile_view, false)
- user = local_assigns.fetch(:user, member.user)
- source = member.source
- can_admin_member = can?(current_user, action_member_permission(:update, member), member)
......@@ -8,45 +9,53 @@
%span.list-item-name
- if user
= image_tag avatar_icon(user, 40), class: "avatar s40", alt: ''
%strong
= link_to user.name, user_path(user)
%span.cgray= user.to_reference
.user-info
= link_to user.name, user_path(user), class: 'member'
%span.cgray= user.to_reference
- if user == current_user
%span.label.label-success.prepend-left-5 It's you
- if user == current_user
%span.label.label-success.prepend-left-5 It's you
- if user.blocked?
%label.label.label-danger
%strong Blocked
- if user.blocked?
%label.label.label-danger
%strong Blocked
- if source.instance_of?(Group) && source != @group
&middot;
= link_to source.full_name, source, class: "member-group-link"
- if source.instance_of?(Group) && source != @group
&middot;
= link_to source.full_name, source, class: "member-group-link"
.hidden-xs.cgray
- if member.request?
Requested
= time_ago_with_tooltip(member.requested_at)
- else
Joined #{time_ago_with_tooltip(member.created_at)}
- 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) }
Expires in #{distance_of_time_in_words_to_now(member.expires_at)}
.cgray
- if member.request?
Requested
= time_ago_with_tooltip(member.requested_at)
- else
Joined #{time_ago_with_tooltip(member.created_at)}
- 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) }
Expires in #{distance_of_time_in_words_to_now(member.expires_at)}
- else
= image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: ''
%strong= member.invite_email
.cgray
Invited
- if member.created_by
by
= link_to member.created_by.name, user_path(member.created_by)
= time_ago_with_tooltip(member.created_at)
.user-info
.member= member.invite_email
.cgray
Invited
- if member.created_by
by
= link_to member.created_by.name, user_path(member.created_by)
= time_ago_with_tooltip(member.created_at)
- if show_roles
- current_resource = @project || @group
.controls.member-controls
- 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
= form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f|
= f.hidden_field :access_level
......@@ -75,13 +84,17 @@
- if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source)
= link_to 'Resend invite', polymorphic_path([:resend_invite, member]),
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
= link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]),
= link_to polymorphic_path([:approve_access_request, member]),
method: :post,
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 current_user == user
......@@ -96,8 +109,9 @@
data: { confirm: remove_member_message(member) },
class: 'btn btn-remove prepend-left-10',
title: remove_member_title(member) do
%span.visible-xs-block
%span{ class: ('visible-xs-block' unless force_mobile_view) }
Delete
= icon('trash', class: 'hidden-xs')
- unless force_mobile_view
= icon('trash', class: 'hidden-xs')
- else
%span.member-access-text= member.human_access
- force_mobile_view = local_assigns.fetch(:force_mobile_view, false)
- if requesters.any?
.panel.panel-default.prepend-top-default
.panel.panel-default.prepend-top-default{ class: ('panel-mobile' if force_mobile_view ) }
.panel-heading
Users requesting access to
%strong= membership_source.name
%span.badge= requesters.size
%ul.content-list
= render partial: 'shared/members/member', collection: requesters, as: :member
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: requesters, as: :member, locals: { force_mobile_view: force_mobile_view }
......@@ -2,18 +2,34 @@ class BackgroundMigrationWorker
include Sidekiq::Worker
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 form:
#
# [migration-class, [arg1, arg2, ...]]
def self.perform_bulk(*jobs)
def self.perform_bulk(jobs)
Sidekiq::Client.push_bulk('class' => self,
'queue' => sidekiq_options['queue'],
'args' => jobs)
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.
#
# 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.
This file is called `resque.yml` for historical reasons. We are **NOT**
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)
module Gitlab
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')
# Settings in config/environments/* take precedence over those specified here.
......@@ -26,7 +28,8 @@ module Gitlab
#{config.root}/app/models/members
#{config.root}/app/models/project_services
#{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")
......@@ -141,15 +144,15 @@ module Gitlab
end
end
# Use Redis caching across all environments
redis_config_hash = Gitlab::Redis.params
redis_config_hash[:namespace] = Gitlab::Redis::CACHE_NAMESPACE
redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
# Use caching across all environments
caching_config_hash = Gitlab::Redis::Cache.params
caching_config_hash[:namespace] = Gitlab::Redis::Cache::CACHE_NAMESPACE
caching_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
if Sidekiq.server? # threaded context
redis_config_hash[:pool_size] = Sidekiq.options[:concurrency] + 5
redis_config_hash[:pool_timeout] = 1
caching_config_hash[:pool_size] = Sidekiq.options[:concurrency] + 5
caching_config_hash[:pool_timeout] = 1
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
......
......@@ -5,12 +5,6 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
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
if ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test'
ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
......
......@@ -42,3 +42,4 @@ test: &test
password:
# host: localhost
# socket: /tmp/mysql.sock
prepared_statements: false
......@@ -46,3 +46,4 @@ test: &test
username: postgres
password:
# host: localhost
prepared_statements: false
......@@ -539,10 +539,15 @@ production: &base
# enabled: true
# host: localhost
# port: 3808
prometheus:
## Monitoring
# Built in monitoring settings
monitoring:
# Time between sampling of unicorn socket metrics, in seconds
# unicorn_sampler_interval: 10
# IP whitelist to access monitoring endpoints
ip_whitelist:
- 127.0.0.0/8
#
# 5. Extra customization
......
......@@ -494,10 +494,11 @@ Settings.webpack.dev_server['host'] ||= 'localhost'
Settings.webpack.dev_server['port'] ||= 3808
#
# Prometheus metrics settings
# Monitoring settings
#
Settings['prometheus'] ||= Settingslogic.new({})
Settings.prometheus['unicorn_sampler_interval'] ||= 10
Settings['monitoring'] ||= Settingslogic.new({})
Settings.monitoring['ip_whitelist'] ||= ['127.0.0.1/8']
Settings.monitoring['unicorn_sampler_interval'] ||= 10
#
# Testing settings
......
# Make sure we initialize a Redis connection pool before Sidekiq starts
# multi-threaded execution.
Gitlab::Redis.with { nil }
# Make sure we initialize a Redis connection pool before multi-threaded
# execution starts by
# 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)
end
# 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|
# 0 should be Sentry to catch errors in this middleware
......
......@@ -3,4 +3,6 @@ require 'flipper/middleware/memoizer'
unless Rails.env.test?
Rails.application.config.middleware.use Flipper::Middleware::Memoizer,
lambda { Feature.flipper }
Feature.register_feature_groups
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::PerformanceBar
......
......@@ -19,12 +19,12 @@ cookie_key = if Rails.env.development?
if Rails.env.test?
Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session"
else
redis_config = Gitlab::Redis.params
redis_config[:namespace] = Gitlab::Redis::SESSION_NAMESPACE
sessions_config = Gitlab::Redis::SharedState.params
sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE
Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks.
servers: redis_config,
servers: sessions_config,
key: cookie_key,
secure: Gitlab.config.gitlab.https,
httponly: true,
......
# Custom Redis configuration
redis_config_hash = Gitlab::Redis.params
redis_config_hash[:namespace] = Gitlab::Redis::SIDEKIQ_NAMESPACE
# Custom Queues configuration
queues_config_hash = Gitlab::Redis::Queues.params
queues_config_hash[:namespace] = Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE
# Default is to retry 25 times with exponential backoff. That's too much.
Sidekiq.default_worker_options = { retry: 3 }
Sidekiq.configure_server do |config|
config.redis = redis_config_hash
config.redis = queues_config_hash
config.server_middleware do |chain|
chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS']
......@@ -54,7 +54,7 @@ Sidekiq.configure_server do |config|
end
Sidekiq.configure_client do |config|
config.redis = redis_config_hash
config.redis = queues_config_hash
config.client_middleware do |chain|
chain.add Gitlab::SidekiqStatus::ClientMiddleware
......@@ -74,5 +74,5 @@ begin
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
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