Commit 317c3cdd authored by Alessio Caiazza's avatar Alessio Caiazza

Merge branch 'refactor-clusters' into 38464-k8s-apps

parents 80b0834a eba27fe0
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-phantomjs-2.1-node-8.x-yarn-1.0-postgresql-9.6"
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-chrome-62.0-node-8.x-yarn-1.2-postgresql-9.6"
.default-cache: &default-cache
key: "ruby-235-with-yarn"
......@@ -23,7 +23,6 @@ variables:
SIMPLECOV: "true"
GIT_DEPTH: "20"
GIT_SUBMODULE_STRATEGY: "none"
PHANTOMJS_VERSION: "2.1.1"
GET_SOURCES_ATTEMPTS: "3"
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json
KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-master.json
......@@ -455,7 +454,7 @@ db:migrate:reset-mysql:
variables:
SETUP_DB: "false"
script:
- git fetch origin v9.3.0
- git fetch https://gitlab.com/gitlab-org/gitlab-ce.git v9.3.0
- git checkout -f FETCH_HEAD
- bundle install $BUNDLE_INSTALL_FLAGS
- cp config/gitlab.yml.example config/gitlab.yml
......@@ -551,7 +550,6 @@ karma:
<<: *dedicated-runner
<<: *except-docs
<<: *pull-cache
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-chrome-61.0-node-8.x-yarn-1.0-postgresql-9.6"
stage: test
variables:
BABEL_ENV: "coverage"
......
......@@ -112,7 +112,7 @@ linters:
# Reports when you define the same selector twice in a single sheet.
MergeableSelector:
enabled: false
enabled: true
# Functions, mixins, variables, and placeholders should be declared
# with all lowercase letters and hyphens instead of underscores.
......
......@@ -2,6 +2,30 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 10.1.1 (2017-10-31)
- [FIXED] Auto Devops kubernetes default namespace is now correctly built out of gitlab project group-name. !14642 (Mircea Danila Dumitrescu)
- [FIXED] Forbid the usage of `Redis#keys`. !14889
- [FIXED] Make the circuitbreaker more robust by adding higher thresholds, and multiple access attempts. !14933
- [FIXED] Only cache last push event for existing projects when pushing to a fork. !14989
- [FIXED] Fix bug preventing secondary emails from being confirmed. !15010
- [FIXED] Fix broken wiki pages that link to a wiki file. !15019
- [FIXED] Don't rename paths that were freed up when upgrading. !15029
- [FIXED] Fix bitbucket login. !15051
- [FIXED] Update gitaly in Gitlab 10.1 to 0.43.1 for temp file cleanup. !15055
- [FIXED] Use the correct visibility attribute for projects in system hooks. !15065
- [FIXED] Normalize LDAP DN when looking up identity.
- [FIXED] Adds callback functions for initial request in clusters page.
- [FIXED] Fix missing Import/Export issue assignees.
- [FIXED] Allow boards as top level route.
- [FIXED] Fix widget of locked merge requests not being presented.
- [FIXED] Fix editing issue description in mobile view.
- [FIXED] Fix deletion of container registry or images returning an error.
- [FIXED] Fix the writing of invalid environment refs.
- [CHANGED] Store circuitbreaker settings in the database instead of config. !14842
- [CHANGED] Update default disabled merge request widget message to reflect a general failure. !14960
- [PERFORMANCE] Stop merge requests with thousands of commits from timing out. !15063
## 10.1.0 (2017-10-22)
- [SECURITY] Use a timeout on certain git operations. !14872
......@@ -194,6 +218,24 @@ entry.
- creation of keys moved to services. !13331 (haseebeqx)
- Add username as GL_USERNAME in hooks.
## 10.0.5 (2017-11-03)
- [FIXED] Fix incorrect X-axis labels in Prometheus graphs. !14258
- [FIXED] Fix `rake gitlab:incoming_email:check` and make it report the actual error. !14423
- [FIXED] Does not check if an invariant hashed storage path exists on disk when renaming projects. !14428
- [FIXED] Fix bottom spacing for dropdowns that open upwards. !14535
- [FIXED] Fix the project import with issues and milestones. !14657
- [FIXED] Fix broken Y-axis scaling in some Prometheus graphs. !14693
- [FIXED] Fixed duplicate notifications when added multiple labels on an issue. !14798
- [FIXED] Don't rename paths that were freed up when upgrading. !15029
- [FIXED] Fixed issue/merge request breadcrumb titles not having links.
- [FIXED] Fix application setting to cache nil object.
- [FIXED] Fix missing Import/Export issue assignees.
- [FIXED] Allow boards as top level route.
- [FIXED] Fixed milestone breadcrumb links.
- [FIXED] Fixed merge request widget merged & closed date tooltip text.
- [FIXED] fix merge request widget status icon for failed CI.
## 10.0.4 (2017-10-16)
- [SECURITY] Move project repositories between namespaces when renaming users.
......
......@@ -324,9 +324,9 @@ group :development, :test do
# Generate Fake data
gem 'ffaker', '~> 2.4'
gem 'capybara', '~> 2.15.0'
gem 'capybara', '~> 2.15'
gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.9.0'
gem 'selenium-webdriver', '~> 3.5'
gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4'
......
......@@ -113,12 +113,13 @@ GEM
mime-types (>= 1.16)
cause (0.1)
charlock_holmes (0.7.5)
childprocess (0.7.0)
ffi (~> 1.0, >= 1.0.11)
chronic (0.10.2)
chronic_duration (0.10.6)
numerizer (~> 0.1.1)
chunky_png (1.3.5)
citrus (3.0.2)
cliver (0.3.2)
coderay (1.1.1)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
......@@ -604,11 +605,6 @@ GEM
pg (0.18.4)
po_to_json (1.0.1)
json (>= 1.6.0)
poltergeist (1.9.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
posix-spawn (0.3.13)
powerpack (0.1.1)
premailer (1.10.4)
......@@ -818,6 +814,9 @@ GEM
activesupport (>= 3.1)
select2-rails (3.5.9.3)
thor (~> 0.14)
selenium-webdriver (3.5.0)
childprocess (~> 0.5)
rubyzip (~> 1.0)
sentry-raven (2.5.3)
faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
......@@ -949,9 +948,6 @@ GEM
hashdiff
webpack-rails (0.9.10)
railties (>= 3.2.0)
websocket-driver (0.6.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
wikicloth (0.8.1)
builder
expression_parser
......@@ -988,7 +984,7 @@ DEPENDENCIES
browser (~> 2.2)
bullet (~> 5.5.0)
bundler-audit (~> 0.5.0)
capybara (~> 2.15.0)
capybara (~> 2.15)
capybara-screenshot (~> 1.0.0)
carrierwave (~> 1.2)
charlock_holmes (~> 0.7.5)
......@@ -1104,7 +1100,6 @@ DEPENDENCIES
peek-redis (~> 1.2.0)
peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2)
poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.7.0.beta18)
pry-byebug (~> 3.4.1)
......@@ -1150,6 +1145,7 @@ DEPENDENCIES
scss_lint (~> 0.54.0)
seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9)
selenium-webdriver (~> 3.5)
sentry-raven (~> 2.5.3)
settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6)
......
......@@ -79,8 +79,6 @@ const Filter = {
this.hook.trigger.addEventListener('keydown.dl', this.eventWrapper.debounceKeydown);
this.hook.trigger.addEventListener('mousedown.dl', this.eventWrapper.debounceKeydown);
this.debounceKeydown({ detail: { hook: this.hook } });
},
destroy: function destroy() {
......
import 'core-js/es6/map';
import 'core-js/es6/set';
import simulateDrag from './simulate_drag';
import simulateInput from './simulate_input';
// Export to global space for rspec to use
window.simulateDrag = simulateDrag;
window.simulateInput = simulateInput;
function triggerEvents(input) {
input.dispatchEvent(new Event('keydown'));
input.dispatchEvent(new Event('keypress'));
input.dispatchEvent(new Event('input'));
input.dispatchEvent(new Event('keyup'));
}
export default function simulateInput(target, text) {
const input = document.querySelector(target);
if (!input || !input.matches('textarea, input')) {
return false;
}
if (text.length > 0) {
Array.prototype.forEach.call(text, (char) => {
input.value += char;
triggerEvents(input);
});
} else {
triggerEvents(input);
}
return true;
}
......@@ -40,6 +40,10 @@
&.top-block {
border-top: none;
.container-fluid {
background-color: inherit;
}
}
&.middle-block {
......@@ -98,10 +102,6 @@
background-color: $white-light;
border-top: none;
}
&.top-block .container-fluid {
background-color: inherit;
}
}
.sub-header-block {
......
......@@ -12,15 +12,15 @@
border-left: 3px solid $border-color;
color: $text-color;
background: $gray-light;
}
.bs-callout h4 {
h4 {
margin-top: 0;
margin-bottom: 5px;
}
}
.bs-callout p:last-child {
p:last-child {
margin-bottom: 0;
}
}
/* Variations */
......
......@@ -53,6 +53,14 @@ hr {
.str-truncated {
@include str-truncated;
&-60 {
@include str-truncated(60%);
}
&-100 {
@include str-truncated(100%);
}
}
.block-truncated {
......@@ -78,10 +86,17 @@ hr {
font-size: 14px;
}
table a code {
table {
a code {
position: relative;
top: -2px;
margin-right: 3px;
}
td.permission-x {
background: $table-permission-x-bg !important;
text-align: center;
}
}
.loading {
......@@ -266,13 +281,6 @@ img.emoji {
margin-bottom: 10px;
}
table {
td.permission-x {
background: $table-permission-x-bg !important;
text-align: center;
}
}
.btn-sign-in {
text-shadow: none;
......@@ -338,10 +346,11 @@ table {
.dropzone .dz-preview .dz-progress {
border-color: $border-color !important;
}
.dropzone .dz-preview .dz-progress .dz-upload {
.dz-upload {
background: $gl-success !important;
}
}
.dz-message {
......@@ -402,16 +411,6 @@ table {
border-radius: $border-radius-default;
}
.str-truncated {
&-60 {
@include str-truncated(60%);
}
&-100 {
@include str-truncated(100%);
}
}
.tooltip {
.tooltip-inner {
word-wrap: break-word;
......
......@@ -141,17 +141,17 @@
svg {
fill: $gl-text-color-secondary;
}
}
.nav-item-name {
flex: 1;
}
li.active {
&.active {
> a {
font-weight: $gl-font-weight-bold;
}
}
}
@media (max-width: $screen-xs-max) {
left: (-$contextual-sidebar-width);
......@@ -484,10 +484,7 @@
height: calc(100vh - #{$header-height});
@media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS
// scss-lint:disable DuplicateProperty
height: calc(100vh - 180px);
// scss-lint:enable DuplicateProperty
}
}
......
......@@ -727,11 +727,11 @@
.pika-single.animate-picker.is-bound {
@include set-visible;
}
.pika-single.animate-picker.is-bound.is-hidden {
&.is-hidden {
@include set-invisible;
overflow: hidden;
}
}
@mixin dropdown-item-hover {
......@@ -938,9 +938,7 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
border-right: 0;
}
}
}
.projects-dropdown-container {
.projects-list-frequent-container,
.projects-list-search-container, {
padding: 8px 0;
......@@ -951,11 +949,6 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
.projects-list-frequent-container li.section-empty,
.projects-list-search-container li.section-empty {
padding: 0 15px;
}
.section-header,
.projects-list-frequent-container li.section-empty,
.projects-list-search-container li.section-empty {
color: $gl-text-color-secondary;
font-size: $gl-font-size;
}
......
......@@ -165,9 +165,8 @@
&:last-child {
border-right: none;
}
}
td.blame-commit {
&.blame-commit {
padding: 5px 10px;
min-width: 400px;
max-width: 400px;
......@@ -184,19 +183,7 @@
}
}
@for $i from 0 through 5 {
td.blame-commit-age-#{$i} {
border-left-color: mix($blame-cyan, $blame-blue, $i / 5.0 * 100%);
}
}
@for $i from 1 through 4 {
td.blame-commit-age-#{$i + 5} {
border-left-color: mix($blame-gray, $blame-cyan, $i / 4.0 * 100%);
}
}
td.line-numbers {
&.line-numbers {
float: none;
border-left: 1px solid $blame-line-numbers-border;
......@@ -206,11 +193,24 @@
}
}
td.lines {
&.lines {
padding: 0;
}
}
@for $i from 0 through 5 {
td.blame-commit-age-#{$i} {
border-left-color: mix($blame-cyan, $blame-blue, $i / 5.0 * 100%);
}
}
@for $i from 1 through 4 {
td.blame-commit-age-#{$i + 5} {
border-left-color: mix($blame-gray, $blame-cyan, $i / 4.0 * 100%);
}
}
}
&.logs {
background: $logs-bg;
max-height: 700px;
......
......@@ -268,12 +268,6 @@
.filtered-search-box-input-container {
flex: 1;
position: relative;
// Fix PhantomJS not supporting `flex: 1;` properly.
// This is important because it can change the expected `e.target` when clicking things in tests.
// See https://gitlab.com/gitlab-org/gitlab-ce/blob/b54acba8b732688c59fe2f38510c469dc86ee499/spec/features/issues/filtered_search/visual_tokens_spec.rb#L61
// - With `width: 100%`: `e.target` = `.tokens-container`, https://i.imgur.com/jGq7wbx.png
// - Without `width: 100%`: `e.target` = `.filtered-search`, https://i.imgur.com/cNI2CyT.png
width: 100%;
min-width: 0;
}
......@@ -469,10 +463,10 @@
word-break: break-all;
}
}
}
.filter-dropdown-item.droplab-item-active .btn {
&.droplab-item-active .btn {
@extend %filter-dropdown-item-btn-hover;
}
}
.filter-dropdown-loading {
......
......@@ -352,7 +352,77 @@
.header-user .dropdown-menu-nav,
.header-new .dropdown-menu-nav {
margin-top: $dropdown-vertical-offset;
margin-top: 4px;
}
.search {
margin: 4px 8px 0;
form {
height: 32px;
border: 0;
border-radius: $border-radius-default;
transition: border-color ease-in-out 0.15s, background-color ease-in-out 0.15s;
&:hover {
box-shadow: none;
}
}
.search-input {
color: $white-light;
background: none;
transition: color ease-in-out 0.15s;
}
.search-input::placeholder {
transition: color ease-in-out 0.15s;
}
.location-badge {
font-size: 12px;
margin: -4px 4px -4px -4px;
line-height: 25px;
padding: 4px 8px;
border-radius: 2px 0 0 2px;
height: 32px;
transition: border-color ease-in-out 0.15s;
}
&.search-active {
form {
background-color: rgba($indigo-200, .3);
box-shadow: none;
.search-input {
color: $gl-text-color;
transition: color ease-in-out 0.15s;
}
.search-input::placeholder {
color: $gl-text-color-tertiary;
}
.search-input-wrap {
.search-icon,
.clear-icon {
color: $gl-text-color-tertiary;
transition: color ease-in-out 0.15s;
}
}
}
.location-badge {
background-color: $nav-badge-bg;
border-color: $border-color;
}
.search-input-wrap {
.clear-icon {
color: $white-light;
}
}
}
}
.breadcrumbs {
......
......@@ -30,10 +30,10 @@ body {
.container {
padding-top: 0;
z-index: 5;
}
.container .content {
.content {
margin: 0;
}
}
.navless-container {
......@@ -82,26 +82,26 @@ body {
transition: background-color 0.15s, border-color 0.15s;
background-color: $orange-500;
border-color: $orange-500;
&:only-of-type {
background-color: $orange-500;
border-color: $orange-500;
}
.alert-warning + .alert-warning {
+ .alert-warning {
background-color: $orange-600;
border-color: $orange-600;
}
.alert-warning + .alert-warning + .alert-warning {
+ .alert-warning {
background-color: $orange-700;
border-color: $orange-700;
}
.alert-warning + .alert-warning + .alert-warning + .alert-warning {
+ .alert-warning {
background-color: $orange-800;
border-color: $orange-800;
}
.alert-warning:only-of-type {
background-color: $orange-500;
border-color: $orange-500;
}
}
}
}
......
......@@ -299,7 +299,8 @@ ul.indent-list {
}
}
.group-list-tree .avatar-container.content-loading {
.group-list-tree {
.avatar-container.content-loading {
position: relative;
> a,
......@@ -310,15 +311,15 @@ ul.indent-list {
> a {
padding: 2px;
}
> a .avatar {
.avatar {
border: 2px solid $white-normal;
&.identicon {
line-height: 30px;
}
}
}
&::after {
content: "";
......@@ -330,9 +331,8 @@ ul.indent-list {
border-radius: 50%;
animation: spin-avatar 3s infinite linear;
}
}
}
.group-list-tree {
.folder-toggle-wrap {
float: left;
line-height: $list-text-height;
......
......@@ -173,21 +173,8 @@
ul > li {
white-space: nowrap;
}
}
@media(max-width: $screen-xs-max) {
.atwho-view-ul {
width: 350px;
}
.atwho-view ul li {
overflow: hidden;
text-overflow: ellipsis;
}
}
// TODO: fallback to global style
.atwho-view {
// TODO: fallback to global style
.atwho-view-ul {
padding: 8px 1px;
......@@ -220,3 +207,14 @@
}
}
}
@media(max-width: $screen-xs-max) {
.atwho-view-ul {
width: 350px;
}
.atwho-view ul li {
overflow: hidden;
text-overflow: ellipsis;
}
}
......@@ -25,8 +25,11 @@
margin: 0;
padding: $gl-padding 0;
border: none;
&:not(:last-child) {
border-bottom: 1px solid $white-normal;
}
}
}
.gl-responsive-table-row-col-span {
......
......@@ -340,11 +340,64 @@
}
}
.project-item-select-holder.btn-group {
.page-with-layout-nav {
.right-sidebar {
top: ($header-height + 1) * 2;
}
&.page-with-sub-nav {
.right-sidebar {
top: ($header-height + 1) * 3;
&.affix {
top: $header-height;
}
}
}
}
.with-performance-bar .page-with-layout-nav {
.right-sidebar {
top: ($header-height + 1) * 2 + $performance-bar-height;
}
&.page-with-sub-nav {
.right-sidebar {
top: ($header-height + 1) * 3 + $performance-bar-height;
&.affix {
top: $header-height + $performance-bar-height;
}
}
}
}
@media (max-width: $screen-xs-max) {
.top-area {
flex-flow: row wrap;
.nav-controls {
$controls-margin: $btn-xs-side-margin - 2px;
flex: 0 0 100%;
&.controls-flex {
display: flex;
max-width: 350px;
overflow: hidden;
float: right;
flex-flow: row wrap;
align-items: center;
justify-content: center;
padding: 0 0 $gl-padding-top;
}
.controls-item,
.controls-item-full,
.controls-item:last-child {
flex: 1 1 35%;
display: block;
width: 100%;
margin: $controls-margin;
}
}
}
.new-project-item-link {
white-space: nowrap;
......
......@@ -60,22 +60,12 @@
border-radius: $border-radius-base;
border: 1px solid $dropdown-border-color;
min-width: 175px;
color: $gl-text-color;
z-index: 999;
}
.select2-drop-mask {
z-index: 998;
color: $gl-grayish-blue;
}
.select2-drop.select2-drop-above.select2-drop-active {
border-top: 1px solid $dropdown-border-color;
margin-top: -6px;
}
.select2-results li.select2-result-with-children > .select2-result-label {
font-weight: $gl-font-weight-bold;
color: $gl-text-color;
.select2-results .select2-result-label,
.select2-more-results {
padding: 10px 15px;
}
.select2-container-active {
......@@ -144,9 +134,8 @@
.select2-drop-auto-width & {
padding: 15px 15px 5px;
}
}
.select2-search input {
input {
padding: 2px 25px 2px 5px;
background: $white-light image-url('select2.png');
background-repeat: no-repeat;
......@@ -158,44 +147,33 @@
&:focus {
border-color: $input-border-focus;
}
}
.select2-search input.select2-active {
&.select2-active {
background-color: $white-light;
background-image: image-url('select2-spinner.gif') !important;
background-repeat: no-repeat;
background-position: right 5px center !important;
background-size: 16px 16px !important;
}
}
}
.select2-results .select2-no-results,
.select2-results .select2-searching,
.select2-results .select2-ajax-error,
.select2-results .select2-selection-limit {
background: $gray-light;
display: list-item;
padding: 10px 15px;
}
.select2-results {
margin: 0;
padding: #{$gl-padding / 2} 0;
.select2-no-results,
.select2-searching,
.select2-ajax-error,
.select2-selection-limit {
background: transparent;
padding: #{$gl-padding / 2} $gl-padding;
}
padding: 10px 0;
.select2-result-label,
.select2-more-results {
padding: #{$gl-padding / 2} $gl-padding;
}
.select2-highlighted {
background: transparent;
li.select2-result-with-children > .select2-result-label {
font-weight: $gl-font-weight-bold;
color: $gl-text-color;
.select2-result-label {
background: $dropdown-item-hover-bg;
}
}
.select2-result {
padding: 0 1px;
}
}
......@@ -212,6 +190,8 @@
}
.select2-highlighted {
background: $gl-link-color !important;
.group-result {
.group-path {
color: $white-light;
......
......@@ -217,13 +217,31 @@ $white-gc-bg: #eaf2f5;
.cp { color: $white-cp; font-weight: $gl-font-weight-bold; }
.c1 { color: $white-c1; font-style: italic; }
.cs { color: $white-cs; font-weight: $gl-font-weight-bold; font-style: italic; }
.gd { color: $white-gd; background-color: $white-gd-bg; }
.gd .x { color: $white-gd-x; background-color: $white-gd-x-bg; }
.gd {
color: $white-gd;
background-color: $white-gd-bg;
.x {
color: $white-gd-x;
background-color: $white-gd-x-bg;
}
}
.ge { font-style: italic; }
.gr { color: $white-gr; }
.gh { color: $white-gh; }
.gi { color: $white-gi; background-color: $white-gi-bg; }
.gi .x { color: $white-gi-x; background-color: $white-gi-x-bg; }
.gi {
color: $white-gi;
background-color: $white-gi-bg;
.x {
color: $white-gi-x;
background-color: $white-gi-x-bg;
}
}
.go { color: $white-go; }
.gp { color: $white-gp; }
.gs { font-weight: $gl-font-weight-bold; }
......
......@@ -158,13 +158,31 @@ span.highlight_word {
.cp { color: $highlighted-cp; font-weight: $gl-font-weight-bold; }
.c1 { color: $highlighted-c1; font-style: italic; }
.cs { color: $highlighted-cs; font-weight: $gl-font-weight-bold; font-style: italic; }
.gd { color: $highlighted-gd; background-color: $highlighted-gd-bg; }
.gd .x { color: $highlighted-gd; background-color: $highlighted-gd-x-bg; }
.gd {
color: $highlighted-gd;
background-color: $highlighted-gd-bg;
.x {
color: $highlighted-gd;
background-color: $highlighted-gd-x-bg;
}
}
.ge { font-style: italic; }
.gr { color: $highlighted-gr; }
.gh { color: $highlighted-gh; }
.gi { color: $highlighted-gi; background-color: $highlighted-gi-bg; }
.gi .x { color: $highlighted-gi; background-color: $highlighted-gi-x-bg; }
.gi {
color: $highlighted-gi;
background-color: $highlighted-gi-bg;
.x {
color: $highlighted-gi;
background-color: $highlighted-gi-x-bg;
}
}
.go { color: $highlighted-go; }
.gp { color: $highlighted-gp; }
.gs { font-weight: $gl-font-weight-bold; }
......
......@@ -86,10 +86,7 @@
}
@media (min-width: $screen-md-min) {
height: 475px; // Needed for PhantomJS
// scss-lint:disable DuplicateProperty
height: calc(100vh - 160px);
// scss-lint:enable DuplicateProperty
min-height: 475px;
}
}
......
......@@ -68,19 +68,19 @@
&.affix {
top: $header-height;
}
// with sidebar
&.affix.sidebar-expanded {
&.sidebar-expanded {
right: 306px;
left: 16px;
}
// without sidebar
&.affix.sidebar-collapsed {
&.sidebar-collapsed {
right: 16px;
left: 16px;
}
}
&.affix-top {
position: absolute;
......
......@@ -22,6 +22,11 @@
}
}
}
svg {
width: 136px;
height: 136px;
}
}
.col-headers {
......@@ -155,11 +160,6 @@
}
}
.landing svg {
width: 136px;
height: 136px;
}
.fa-spinner {
font-size: 28px;
position: relative;
......
......@@ -380,6 +380,10 @@
}
}
}
.line_content {
white-space: pre-wrap;
}
}
.file-content .diff-file {
......@@ -387,10 +391,6 @@
border: none;
}
.diff-file .line_content {
white-space: pre-wrap;
}
.diff-wrap-lines .line_content {
white-space: pre-wrap;
}
......
......@@ -255,23 +255,6 @@
width: 100%;
padding: 0;
padding-bottom: 100%;
}
.prometheus-svg-container > svg {
position: absolute;
height: 100%;
width: 100%;
left: 0;
top: 0;
text {
fill: $gl-text-color;
stroke-width: 0;
}
.text-metric-bold {
font-weight: $gl-font-weight-bold;
}
.label-axis-text {
fill: $black;
......@@ -286,19 +269,27 @@
font-size: 12px;
}
.legend-axis-text {
> svg {
position: absolute;
height: 100%;
width: 100%;
left: 0;
top: 0;
.label-axis-text,
.text-metric-usage {
fill: $black;
font-weight: $gl-font-weight-normal;
font-size: 12px;
}
.tick {
> line {
stroke: $gray-darker;
.legend-axis-text {
fill: $black;
}
> text {
.tick > text {
font-size: 12px;
}
}
.text-metric-title {
font-size: 12px;
......@@ -324,4 +315,5 @@
font-size: 8px;
}
}
}
}
......@@ -127,7 +127,16 @@
}
.right-sidebar {
a:not(.btn-retry),
position: absolute;
top: $header-height;
bottom: 0;
right: 0;
transition: width .3s;
background: $gray-light;
z-index: 200;
overflow: hidden;
a,
.btn-link {
color: inherit;
}
......@@ -228,17 +237,6 @@
.btn-clipboard:hover {
color: $gl-text-color;
}
}
.right-sidebar {
position: absolute;
top: $header-height;
bottom: 0;
right: 0;
transition: width $right-sidebar-transition-duration;
background: $gray-light;
z-index: 200;
overflow: hidden;
.issuable-sidebar {
width: calc(100% + 100px);
......
......@@ -109,6 +109,30 @@
border-top-right-radius: $border-radius-default;
border-top-left-radius: $border-radius-default;
// Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long).
// These styles prevent this from breaking the layout, and only applied when providers are configured.
&.custom-provider-tabs {
flex-wrap: wrap;
li {
min-width: 85px;
flex-basis: auto;
// This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen.
// We are making somewhat of an assumption about the configuration here: that users do not have more than
// 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any
// of them. If either condition is false, this will work as expected. If both are true, there may be a missing border
// above one of the bottom row elements. If you know a better way, please implement it!
&:nth-child(n+5) {
border-top: 1px solid $border-color;
}
}
a {
font-size: 16px;
}
}
li {
flex: 1;
text-align: center;
......@@ -154,32 +178,6 @@
}
}
// Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long).
// These styles prevent this from breaking the layout, and only applied when providers are configured.
.new-session-tabs.custom-provider-tabs {
flex-wrap: wrap;
li {
min-width: 85px;
flex-basis: auto;
// This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen.
// We are making somewhat of an assumption about the configuration here: that users do not have more than
// 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any
// of them. If either condition is false, this will work as expected. If both are true, there may be a missing border
// above one of the bottom row elements. If you know a better way, please implement it!
&:nth-child(n+5) {
border-top: 1px solid $border-color;
}
}
a {
font-size: 16px;
}
}
.form-control {
&:active,
&:focus {
......@@ -231,10 +229,9 @@
margin: 0;
padding: 0;
height: 100%;
}
// Fixes footer container to bottom of viewport
.devise-layout-html body {
// Fixes footer container to bottom of viewport
body {
// offset height of fixed header + 1 to avoid scroll
height: calc(100% - 51px);
margin: 0;
......@@ -262,4 +259,5 @@
padding: 0 15px 65px;
}
}
}
}
......@@ -49,9 +49,17 @@
width: auto;
}
}
&.existing-title {
@media (min-width: $screen-sm-min) {
float: left;
}
}
}
.member-form-control {
@include new-style-dropdown;
@media (max-width: $screen-xs-max) {
padding-bottom: 5px;
margin-left: 0;
......@@ -64,12 +72,6 @@
line-height: 43px;
}
.member.existing-title {
@media (min-width: $screen-sm-min) {
float: left;
}
}
.member-search-form {
@include new-style-dropdown;
......@@ -281,7 +283,3 @@
}
}
}
.member-form-control {
@include new-style-dropdown;
}
......@@ -156,6 +156,10 @@
&.media > *:first-child {
margin-right: 10px;
}
.approve-btn {
margin-right: 5px;
}
}
.mr-widget-pipeline-graph {
......@@ -191,6 +195,10 @@
overflow: hidden;
word-break: break-all;
&.media > *:first-child {
margin-right: 10px;
}
&.label-truncated {
position: relative;
display: inline-block;
......@@ -208,14 +216,7 @@
background-color: $gray-light;
}
}
}
.mr-widget-help {
padding: 10px 16px 10px 48px;
font-style: italic;
}
.mr-widget-body {
h4 {
float: left;
font-weight: $gl-font-weight-bold;
......@@ -238,6 +239,10 @@
margin-right: 7px;
}
.approve-btn {
margin-right: 5px;
}
label {
font-weight: $gl-font-weight-normal;
}
......@@ -337,6 +342,22 @@
}
}
.mini-pipeline-graph-dropdown-menu .mini-pipeline-graph-dropdown-item {
display: flex;
align-items: center;
.ci-status-text,
.ci-status-icon {
top: 0;
margin-right: 10px;
}
}
.mr-widget-help {
padding: 10px 16px 10px 48px;
font-style: italic;
}
.ci-coverage {
float: right;
}
......@@ -351,12 +372,6 @@
}
}
.mr-state-widget .mr-widget-body {
.approve-btn {
margin-right: 5px;
}
}
.mr-widget-body-controls {
flex-wrap: wrap;
}
......@@ -470,9 +485,8 @@
padding-bottom: 0;
}
}
}
.mr-info-list.mr-memory-usage {
&.mr-memory-usage {
p {
float: left;
}
......@@ -481,6 +495,7 @@
float: left;
margin-left: 5px;
}
}
}
.mr-source-target {
......
......@@ -66,6 +66,15 @@
height: 6px;
margin: 0;
}
.sidebar-collapsed-icon {
clear: both;
padding: 15px 5px 5px;
.progress {
margin: 5px 0;
}
}
}
.collapsed-milestone-date {
......@@ -93,17 +102,6 @@
margin-right: 0;
}
.milestone-progress {
.sidebar-collapsed-icon {
clear: both;
padding: 15px 5px 5px;
.progress {
margin: 5px 0;
}
}
}
.right-sidebar-collapsed & {
.reference {
border-top: 1px solid $border-gray-normal;
......@@ -156,18 +154,16 @@
.status-box {
margin-top: 0;
order: 1;
}
.milestone-buttons {
margin-left: auto;
}
order: 2;
.status-box {
order: 1;
.verbose {
display: none;
}
.milestone-buttons {
order: 2;
}
.header-text-content {
......@@ -175,10 +171,6 @@
width: 100%;
}
.milestone-buttons .verbose {
display: none;
}
@media (min-width: $screen-xs-min) {
.milestone-buttons .verbose {
display: inline;
......
......@@ -111,24 +111,9 @@
margin: auto;
align-items: center;
.icon {
margin-right: $issuable-warning-icon-margin;
}
}
.disabled-comment .issuable-note-warning {
border: none;
border-radius: $label-border-radius;
padding-top: $gl-vert-padding;
padding-bottom: $gl-vert-padding;
.icon svg {
position: relative;
top: 2px;
margin-right: $btn-xs-side-margin;
width: $gl-font-size;
height: $gl-font-size;
fill: $orange-600;
+ .md-area {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}
......@@ -155,11 +140,6 @@
}
}
.issuable-note-warning + .md-area {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.discussion-form {
background-color: $white-light;
}
......
......@@ -312,7 +312,21 @@ ul.notes {
}
}
.diff-file .notes_holder {
.diff-file {
.is-over {
.add-diff-note {
display: inline-block;
}
}
// Merge request notes in diffs
// Diff is inline
.notes_content .note-header .note-headline-light {
display: inline-block;
position: relative;
}
.notes_holder {
font-family: $regular_font;
td {
......@@ -366,6 +380,7 @@ ul.notes {
}
}
}
}
}
.discussion-header,
......@@ -457,8 +472,9 @@ ul.notes {
margin-left: 10px;
color: $gray-darkest;
.btn-group > .discussion-next-btn {
margin-left: -1px;
@include notes-media('max', $screen-md-max) {
float: none;
margin-left: 0;
}
}
......@@ -469,8 +485,6 @@ ul.notes {
flex-shrink: 0;
display: inline-flex;
align-items: center;
// For PhantomJS that does not support flex
float: right;
margin-left: 10px;
color: $gray-darkest;
......@@ -481,7 +495,6 @@ ul.notes {
}
.more-actions {
float: right; // phantomjs fallback
display: flex;
align-items: flex-end;
......@@ -502,13 +515,6 @@ ul.notes {
min-width: 180px;
}
.discussion-actions {
@include notes-media('max', $screen-md-max) {
float: none;
margin-left: 0;
}
}
.note-actions-item {
margin-left: 12px;
display: flex;
......@@ -665,14 +671,6 @@ ul.notes {
}
}
.diff-file {
.is-over {
.add-diff-note {
display: inline-block;
}
}
}
.disabled-comment {
background-color: $gray-light;
border-radius: $border-radius-base;
......@@ -714,9 +712,8 @@ ul.notes {
svg path {
fill: $gray-darkest;
}
}
.btn.discussion-create-issue-btn {
&.discussion-create-issue-btn {
margin-left: -4px;
border-radius: 0;
border-right: 0;
......@@ -731,6 +728,7 @@ ul.notes {
}
}
}
}
}
.line-resolve-all {
......@@ -801,12 +799,3 @@ ul.notes {
.line-resolve-text {
vertical-align: middle;
}
// Merge request notes in diffs
.diff-file {
// Diff is inline
.notes_content .note-header .note-headline-light {
display: inline-block;
position: relative;
}
}
......@@ -175,6 +175,25 @@
}
}
/**
* Play button with icon in dropdowns
*/
.no-btn {
border: none;
background: none;
outline: none;
width: 100%;
text-align: left;
.icon-play {
position: relative;
top: 2px;
margin-right: 5px;
height: 13px;
width: 12px;
}
}
.duration,
.finished-at {
color: $gl-text-color-secondary;
......@@ -450,8 +469,13 @@
@extend .build-content:hover;
}
.ci-action-icon-container {
position: absolute;
right: 5px;
top: 5px;
// Action Icons in big pipeline-graph nodes
.ci-action-icon-container.ci-action-icon-wrapper {
&.ci-action-icon-wrapper {
height: 30px;
width: 30px;
background: $white-light;
......@@ -462,6 +486,10 @@
&:hover {
background-color: $stage-hover-bg;
border: 1px solid $dropdown-toggle-active-border-color;
svg {
fill: $gl-text-color;
}
}
svg {
......@@ -480,16 +508,7 @@
left: 8px;
}
}
&:hover svg {
fill: $gl-text-color;
}
}
.ci-action-icon-container {
position: absolute;
right: 5px;
top: 5px;
}
.ci-status-icon svg {
......@@ -735,43 +754,44 @@ button.mini-pipeline-graph-dropdown-toggle {
left: -3px;
position: relative;
top: -2px;
}
&:hover svg,
&:focus svg {
fill: $gl-text-color;
}
&.icon-action-retry,
&.icon-action-play {
svg {
width: #{$ci-action-icon-size - 6};
height: #{$ci-action-icon-size - 6};
left: 8px;
}
}
svg.icon-action-stop,
svg.icon-action-cancel {
&.icon-action-stop,
&.icon-action-cancel {
width: 12px;
height: 12px;
top: 1px;
left: -1px;
}
svg.icon-action-play {
&.icon-action-play {
width: 11px;
height: 11px;
top: 1px;
left: 1px;
}
svg.icon-action-retry {
&.icon-action-retry {
width: 16px;
height: 16px;
top: 0;
left: -3px;
}
}
&:hover svg,
&:focus svg {
fill: $gl-text-color;
}
&.icon-action-retry,
&.icon-action-play {
svg {
width: #{$ci-action-icon-size - 6};
height: #{$ci-action-icon-size - 6};
left: 8px;
}
}
}
......@@ -840,13 +860,10 @@ button.mini-pipeline-graph-dropdown-toggle {
left: 100%;
top: -10px;
box-shadow: 0 1px 5px $black-transparent;
}
/**
/**
* Top arrow in the dropdown in the big pipeline graph
*/
.big-pipeline-graph-dropdown-menu {
&::before,
&::after {
content: '';
......@@ -908,12 +925,11 @@ button.mini-pipeline-graph-dropdown-toggle {
margin-top: 1px;
border-bottom-color: $white-light;
}
}
/**
/**
* Center dropdown menu in mini graph
*/
.mini-pipeline-graph-dropdown-menu.dropdown-menu {
&.dropdown-menu {
transform: translate(-80%, 0);
min-width: 150px;
......@@ -923,7 +939,9 @@ button.mini-pipeline-graph-dropdown-toggle {
left: 50%;
min-width: 240px;
}
}
}
/**
* Terminal
*/
......@@ -947,25 +965,6 @@ button.mini-pipeline-graph-dropdown-toggle {
}
}
/**
* Play button with icon in dropdowns
*/
.ci-table .no-btn {
border: none;
background: none;
outline: none;
width: 100%;
text-align: left;
.icon-play {
position: relative;
top: 2px;
margin-right: 5px;
height: 13px;
width: 12px;
}
}
.ci-header-container {
min-height: 55px;
......
......@@ -88,7 +88,8 @@
transition: background 2s ease-out;
&:disabled {
opacity: 0.75;
opacity: 0.5;
pointer-events: none;
}
.highlight-changes & {
......@@ -778,9 +779,8 @@ a.deploy-project-label {
.nav {
padding-top: 12px;
padding-bottom: 12px;
}
.nav > li {
> li {
display: inline-block;
&:not(:last-child) {
......@@ -795,9 +795,8 @@ a.deploy-project-label {
float: right;
}
}
}
.nav > li > a {
> a {
padding: 0;
background-color: transparent;
font-size: 14px;
......@@ -809,6 +808,8 @@ a.deploy-project-label {
color: $gl-text-color;
}
}
}
}
li.missing {
border: 1px dashed $border-gray-normal-dashed;
......@@ -1160,13 +1161,6 @@ pre.light-well {
}
}
.project-repo-select {
&.disabled {
opacity: 0.5;
pointer-events: none;
}
}
.variables-table {
table-layout: fixed;
......
......@@ -78,6 +78,10 @@ input[type="checkbox"]:hover {
}
.search-input-wrap {
// Fallback if flexbox is not supported
display: inline-block;
width: 100%;
.search-icon,
.clear-icon {
position: absolute;
......
......@@ -241,11 +241,11 @@
margin-left: 5px;
background: $badge-bg;
}
}
/* Ensure we don't add border if there's only single li */
li + li {
+ li {
border-top: 1px solid $border-color;
}
}
}
}
......@@ -5,11 +5,11 @@ table .sherlock-code {
.sherlock-code {
pre {
word-wrap: normal;
}
pre code {
code {
white-space: pre;
}
}
}
.sherlock-line-samples-table {
......@@ -21,13 +21,13 @@ table .sherlock-code {
text-align: right;
padding: 0 10px !important;
}
.slow {
color: $red-500;
font-weight: $gl-font-weight-bold;
}
}
.sherlock-file-sample pre {
padding-top: 28px !important;
}
.sherlock-line-samples-table .slow {
color: $red-500;
font-weight: $gl-font-weight-bold;
}
......@@ -40,17 +40,17 @@
@media (max-width: $screen-xs-max) {
width: 100%;
}
}
.person .spark {
.spark {
display: block;
background: $stat-graph-common-bg;
width: 100%;
}
.person .area-contributor {
.area-contributor {
fill: $stat-graph-orange-fill;
}
}
}
.selection rect {
......
......@@ -161,11 +161,11 @@ ul.wiki-pages-list.content-list {
list-style: none;
margin-left: 0;
padding-left: 15px;
}
ul li {
li {
padding: 5px 0;
}
}
}
.wiki {
......
......@@ -4,11 +4,6 @@
-ms-transition: none !important;
-webkit-transition: none !important;
transition: none !important;
-o-transform: none !important;
-moz-transform: none !important;
-ms-transform: none !important;
-webkit-transform: none !important;
transform: none !important;
-webkit-animation: none !important;
-moz-animation: none !important;
-o-animation: none !important;
......
......@@ -94,10 +94,9 @@ module LfsRequest
@storage_project ||= begin
result = project
loop do
break unless result.forked?
result = result.forked_from_project
end
# TODO: Make this go to the fork_network root immeadiatly
# dependant on the discussion in: https://gitlab.com/gitlab-org/gitlab-ce/issues/39769
result = result.fork_source while result.forked?
result
end
......
......@@ -4,6 +4,7 @@ module NotesActions
included do
before_action :set_polling_interval_header, only: [:index]
before_action :noteable, only: :index
before_action :authorize_admin_note!, only: [:update, :destroy]
before_action :note_project, only: [:create]
end
......@@ -188,7 +189,7 @@ module NotesActions
end
def noteable
@noteable ||= notes_finder.target
@noteable ||= notes_finder.target || render_404
end
def last_fetched_at
......
......@@ -110,7 +110,15 @@ module ProjectsHelper
def remove_fork_project_message(project)
_("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") %
{ forked_from_project: @project.forked_from_project.name_with_namespace }
{ forked_from_project: fork_source_name(project) }
end
def fork_source_name(project)
if @project.fork_source
@project.fork_source.full_name
else
@project.fork_network&.deleted_root_project_name
end
end
def project_nav_tabs
......@@ -140,8 +148,8 @@ module ProjectsHelper
def can_change_visibility_level?(project, current_user)
return false unless can?(current_user, :change_visibility_level, project)
if project.forked?
project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE
if project.fork_source
project.fork_source.visibility_level > Gitlab::VisibilityLevel::PRIVATE
else
true
end
......
......@@ -12,4 +12,8 @@ class ForkNetwork < ActiveRecord::Base
def find_forks_in(other_projects)
projects.where(id: other_projects)
end
def merge_requests
MergeRequest.where(target_project: projects)
end
end
......@@ -42,6 +42,7 @@ class Group < Namespace
after_create :post_create_hook
after_destroy :post_destroy_hook
after_save :update_two_factor_requirement
after_update :path_changed_hook, if: :path_changed?
class << self
def supports_nested_groups?
......@@ -295,6 +296,12 @@ class Group < Namespace
list_of_ids.reverse.map { |group| variables[group.id] }.compact.flatten
end
def full_path_was
return path_was unless has_parent?
"#{parent.full_path}/#{path_was}"
end
private
def update_two_factor_requirement
......@@ -303,6 +310,10 @@ class Group < Namespace
users.find_each(&:update_two_factor_requirement)
end
def path_changed_hook
system_hook_service.execute_hooks_for(self, :rename)
end
def visibility_level_allowed_by_parent
return if visibility_level_allowed_by_parent?
......
......@@ -1043,6 +1043,10 @@ class Project < ActiveRecord::Base
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
def fork_source
forked_from_project || fork_network&.root_project
end
def personal?
!group
end
......@@ -1491,7 +1495,8 @@ class Project < ActiveRecord::Base
{ key: 'CI_PROJECT_PATH', value: full_path, public: true },
{ key: 'CI_PROJECT_PATH_SLUG', value: full_path_slug, public: true },
{ key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true },
{ key: 'CI_PROJECT_URL', value: web_url, public: true }
{ key: 'CI_PROJECT_URL', value: web_url, public: true },
{ key: 'CI_PROJECT_VISIBILITY', value: Gitlab::VisibilityLevel.string_level(visibility_level), public: true }
]
end
......
......@@ -168,6 +168,7 @@ class User < ActiveRecord::Base
before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) }
before_save :check_for_verified_email, if: ->(user) { user.email_changed? && !user.new_record? }
after_save :ensure_namespace_correct
after_update :username_changed_hook, if: :username_changed?
after_destroy :post_destroy_hook
after_commit :update_emails_with_primary_email, on: :update, if: -> { previous_changes.key?('email') }
after_commit :update_invalid_gpg_signatures, on: :update, if: -> { previous_changes.key?('email') }
......@@ -871,6 +872,10 @@ class User < ActiveRecord::Base
end
end
def username_changed_hook
system_hook_service.execute_hooks_for(self, :rename)
end
def post_destroy_hook
log_info("User \"#{name}\" (#{email}) was removed")
system_hook_service.execute_hooks_for(self, :destroy)
......
......@@ -3,18 +3,24 @@ module Projects
def execute
return unless @project.forked?
@project.forked_from_project.lfs_objects.find_each do |lfs_object|
if fork_source = @project.fork_source
fork_source.lfs_objects.find_each do |lfs_object|
lfs_object.projects << @project
end
merge_requests = @project.forked_from_project.merge_requests.opened.from_project(@project)
refresh_forks_count(fork_source)
end
merge_requests = @project.fork_network
.merge_requests
.opened
.where.not(target_project: @project)
.from_project(@project)
merge_requests.each do |mr|
::MergeRequests::CloseService.new(@project, @current_user).execute(mr)
end
refresh_forks_count(@project.forked_from_project)
@project.fork_network_member.destroy
@project.forked_project_link.destroy
end
......
......@@ -35,24 +35,22 @@ class SystemHooksService
data[:old_path_with_namespace] = model.old_path_with_namespace
end
when User
data.merge!({
name: model.name,
email: model.email,
user_id: model.id,
username: model.username
})
data.merge!(user_data(model))
if event == :rename
data[:old_username] = model.username_was
end
when ProjectMember
data.merge!(project_member_data(model))
when Group
owner = model.owner
data.merge!(group_data(model))
if event == :rename
data.merge!(
name: model.name,
path: model.path,
group_id: model.id,
owner_name: owner.respond_to?(:name) ? owner.name : nil,
owner_email: owner.respond_to?(:email) ? owner.email : nil
old_path: model.path_was,
old_full_path: model.full_path_was
)
end
when GroupMember
data.merge!(group_member_data(model))
end
......@@ -104,6 +102,19 @@ class SystemHooksService
}
end
def group_data(model)
owner = model.owner
{
name: model.name,
path: model.path,
full_path: model.full_path,
group_id: model.id,
owner_name: owner.try(:name),
owner_email: owner.try(:email)
}
end
def group_member_data(model)
{
group_name: model.group.name,
......@@ -116,4 +127,13 @@ class SystemHooksService
group_access: model.human_access
}
end
def user_data(model)
{
name: model.name,
email: model.email,
user_id: model.id,
username: model.username
}
end
end
- empty_repo = @project.empty_repo?
- fork_network = @project.fork_network
- forked_from_project = @project.forked_from_project || fork_network&.root_project
.project-home-panel.text-center{ class: ("empty-project" if empty_repo) }
.limit-container-width{ class: container_class }
.avatar-container.s70.project-avatar
......@@ -16,13 +15,13 @@
- if @project.forked?
%p
- if forked_from_project
- if @project.fork_source
#{ s_('ForkedFromProjectPath|Forked from') }
= link_to project_path(forked_from_project) do
= forked_from_project.full_name
= link_to project_path(@project.fork_source) do
= fork_source_name(@project)
- else
- deleted_message = s_('ForkedFromProjectPath|Forked from %{project_name} (deleted)')
= deleted_message % { project_name: fork_network.deleted_root_project_name }
= deleted_message % { project_name: fork_source_name(@project) }
.project-repo-buttons
.count-buttons
......
......@@ -10,9 +10,9 @@
cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason } }
%section.settings
%section.settings.no-animate.expanded
%h4= s_('ClusterIntegration|Enable cluster integration')
.settings-content.expanded
.settings-content
.hidden.js-cluster-error.alert.alert-danger.alert-block.append-bottom-10{ role: 'alert' }
= s_('ClusterIntegration|Something went wrong while creating your cluster on Google Container Engine')
......@@ -49,14 +49,14 @@
.form-group
= field.submit _('Save'), class: 'btn btn-success'
%section.settings#js-cluster-details
%section.settings.no-animate#js-cluster-details{ class: ('expanded' if expanded) }
.settings-header
%h4= s_('ClusterIntegration|Cluster details')
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p= s_('ClusterIntegration|See and edit the details for your cluster')
.settings-content.no-animate{ class: ('expanded' if expanded) }
.settings-content
.form_group.append-bottom-20
%label.append-bottom-10{ for: 'cluter-name' }
......@@ -66,11 +66,11 @@
%span.input-group-addon.clipboard-addon
= clipboard_button(text: @cluster.name, title: s_('ClusterIntegration|Copy cluster name'))
%section.settings#js-cluster-advanced-settings
%section.settings.no-animate#js-cluster-advanced-settings{ class: ('expanded' if expanded) }
.settings-header
%h4= _('Advanced settings')
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p= s_('ClusterIntegration|Manage Cluster integration on your GitLab project')
.settings-content.no-animate{ class: ('expanded' if expanded) }
.settings-content
= render 'advanced_settings'
......@@ -173,7 +173,10 @@
%p
This will remove the fork relationship to source project
= succeed "." do
= link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
- if @project.fork_source
= link_to(fork_source_name(@project), project_path(@project.fork_source))
- else
= fork_source_name(@project)
= form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_project_path(@project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f|
%p
%strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.
......
---
title: Expose project visibility as CI variable - CI_PROJECT_VISIBILITY
merge_request: 15193
author:
type: added
---
title: Stop merge requests with thousands of commits from timing out
merge_request: 15063
author:
type: performance
---
title: Update default disabled merge request widget message to reflect a general failure
merge_request: 14960
author:
type: changed
---
title: 'Fix bug preventing secondary emails from being confirmed'
merge_request: 15010
author:
type: fixed
---
title: Fix editing issue description in mobile view
merge_request:
author:
type: fixed
---
title: Fix bitbucket login
merge_request: 15051
author:
type: fixed
---
title: Normalize LDAP DN when looking up identity
title: Fix double border UI bug on pipelines/environments table and pagination
merge_request:
author:
type: fixed
---
title: Make the circuitbreaker more robust by adding higher thresholds, and multiple
access attempts.
merge_request: 14933
author:
type: fixed
---
title: Store circuitbreaker settings in the database instead of config
merge_request: 14842
author:
type: changed
---
title: Forbid the usage of `Redis#keys`
merge_request: 14889
author:
type: fixed
---
title: Don't rename paths that were freed up when upgrading
merge_request: 15029
author:
type: fixed
---
title: Only cache last push event for existing projects when pushing to a fork
merge_request: 14989
author:
type: fixed
---
title: Use the correct visibility attribute for projects in system hooks
merge_request: 15065
author:
type: fixed
---
title: Fix issues with forked projects of which the source was deleted
merge_request: 15150
author:
type: fixed
---
title: Enable MergeableSelector in scss-lint
merge_request: 12810
author: Takuya Noguchi
---
title: Fix broken wiki pages that link to a wiki file
merge_request: 15019
author:
type: fixed
---
title: Allow boards as top level route
merge_request:
author:
type: fixed
---
title: Fix missing Import/Export issue assignees
title: Fix a migration that adds merge_requests_ff_only_enabled column to MR table
merge_request:
author:
type: fixed
---
title: Adds callback functions for initial request in clusters page
title: Render 404 when polling commit notes without having permissions
merge_request:
author:
type: fixed
---
title: Fix widget of locked merge requests not being presented
merge_request:
author:
type: fixed
---
title: Auto Devops kubernetes default namespace is now correctly built out of gitlab
project group-name
merge_request: 14642
author: Mircea Danila Dumitrescu
type: fixed
---
title: Fix deletion of container registry or images returning an error
merge_request:
author:
type: fixed
---
title: Fix the writing of invalid environment refs
merge_request:
author:
type: fixed
---
title: Add system hooks user_rename and group_rename
merge_request: 15123
author:
type: changed
Rails.application.configure do
# Make sure the middleware is inserted first in middleware chain
config.middleware.insert_before('ActionDispatch::Static', 'Gitlab::Testing::RequestBlockerMiddleware')
config.middleware.insert_before('ActionDispatch::Static', 'Gitlab::Testing::RequestInspectorMiddleware')
# Settings specified here will take precedence over those in config/application.rb
......
......@@ -8,8 +8,12 @@ class AddFastForwardOptionToProject < ActiveRecord::Migration
disable_ddl_transaction!
def up
# We put condition here because of a mistake we made a couple of years ago
# see https://gitlab.com/gitlab-org/gitlab-ce/issues/39382#note_45716103
unless column_exists?(:projects, :merge_requests_ff_only_enabled)
add_column_with_default(:projects, :merge_requests_ff_only_enabled, :boolean, default: false)
end
end
def down
if column_exists?(:projects, :merge_requests_ff_only_enabled)
......
class MigrateGcpClustersToNewClustersArchitectures < ActiveRecord::Migration
DOWNTIME = false
def up
gcp_clusters = ActiveRecord::Base.connection.select_all('SELECT * from gcp_clusters;')
rows_for_clusters = Array.new
rows_for_cluster_projects = Array.new
rows_for_cluster_providers_gcp = Array.new
rows_for_cluster_platforms_kubernetes = Array.new
gcp_clusters.each do |gcp_cluster|
rows_for_clusters << params_for_clusters(gcp_cluster)
rows_for_cluster_projects << params_for_cluster_projects(gcp_cluster)
rows_for_cluster_providers_gcp << params_for_cluster_providers_gcp(gcp_cluster)
rows_for_cluster_platforms_kubernetes << params_for_cluster_platforms_kubernetes(gcp_cluster)
end
Gitlab::Database.bulk_insert('clusters', rows_for_clusters)
Gitlab::Database.bulk_insert('cluster_projects', rows_for_cluster_projects)
Gitlab::Database.bulk_insert('cluster_providers_gcp', rows_for_cluster_providers_gcp)
Gitlab::Database.bulk_insert('cluster_platforms_kubernetes', rows_for_cluster_platforms_kubernetes)
end
def down
execute('DELETE FROM clusters')
end
private
def params_for_clusters(gcp_cluster)
{
id: gcp_cluster['id'],
user_id: gcp_cluster['user_id'],
enabled: gcp_cluster['enabled'],
name: gcp_cluster['gcp_cluster_name'],
provider_type: Clusters::Cluster.provider_types[:gcp],
platform_type: Clusters::Cluster.platform_types[:kubernetes],
created_at: gcp_cluster['created_at'],
updated_at: gcp_cluster['updated_at']
}
end
def params_for_cluster_projects(gcp_cluster)
{
cluster_id: gcp_cluster['id'],
project_id: gcp_cluster['project_id'],
created_at: gcp_cluster['created_at'],
updated_at: gcp_cluster['updated_at']
}
end
def params_for_cluster_providers_gcp(gcp_cluster)
{
cluster_id: gcp_cluster['id'],
status: gcp_cluster['status'],
status_reason: gcp_cluster['status_reason'],
gcp_project_id: gcp_cluster['gcp_project_id'],
zone: gcp_cluster['gcp_cluster_zone'],
num_nodes: gcp_cluster['gcp_cluster_size'],
machine_type: gcp_cluster['gcp_machine_type'],
operation_id: gcp_cluster['gcp_operation_id'],
endpoint: gcp_cluster['endpoint'],
encrypted_access_token: gcp_cluster['encrypted_gcp_token'],
encrypted_access_token_iv: gcp_cluster['encrypted_gcp_token_iv'],
created_at: gcp_cluster['created_at'],
updated_at: gcp_cluster['updated_at']
}
end
def params_for_cluster_platforms_kubernetes(gcp_cluster)
{
cluster_id: gcp_cluster['id'],
api_url: 'https://' + gcp_cluster['endpoint'],
ca_cert: gcp_cluster['ca_cert'],
namespace: gcp_cluster['project_namespace'],
username: gcp_cluster['username'],
encrypted_password: gcp_cluster['encrypted_password'],
encrypted_password_iv: gcp_cluster['encrypted_password_iv'],
encrypted_token: gcp_cluster['encrypted_kubernetes_token'],
encrypted_token_iv: gcp_cluster['encrypted_kubernetes_token_iv'],
created_at: gcp_cluster['created_at'],
updated_at: gcp_cluster['updated_at']
}
end
end
class MigrateGcpClustersToNewClustersArchitectures < ActiveRecord::Migration
DOWNTIME = false
class GcpCluster < ActiveRecord::Base
self.table_name = 'gcp_clusters'
belongs_to :project, class_name: 'Project'
include EachBatch
end
class Cluster < ActiveRecord::Base
self.table_name = 'clusters'
has_many :cluster_projects, class_name: 'ClustersProject'
has_many :projects, through: :cluster_projects, class_name: 'Project'
has_one :provider_gcp, class_name: 'ProvidersGcp'
has_one :platform_kubernetes, class_name: 'PlatformsKubernetes'
accepts_nested_attributes_for :provider_gcp
accepts_nested_attributes_for :platform_kubernetes
enum platform_type: {
kubernetes: 1
}
enum provider_type: {
user: 0,
gcp: 1
}
end
class Project < ActiveRecord::Base
self.table_name = 'projects'
has_one :cluster_project, class_name: 'ClustersProject'
has_one :cluster, through: :cluster_project, class_name: 'Cluster'
end
class ClustersProject < ActiveRecord::Base
self.table_name = 'cluster_projects'
belongs_to :cluster, class_name: 'Cluster'
belongs_to :project, class_name: 'Project'
end
class ProvidersGcp < ActiveRecord::Base
self.table_name = 'cluster_providers_gcp'
end
class PlatformsKubernetes < ActiveRecord::Base
self.table_name = 'cluster_platforms_kubernetes'
end
def up
GcpCluster.all.find_each(batch_size: 1) do |gcp_cluster|
Cluster.create(
enabled: gcp_cluster.enabled,
user_id: gcp_cluster.user_id,
name: gcp_cluster.gcp_cluster_name,
provider_type: Cluster.provider_types[:gcp],
platform_type: Cluster.platform_types[:kubernetes],
projects: [gcp_cluster.project],
provider_gcp_attributes: {
status: gcp_cluster.status,
status_reason: gcp_cluster.status_reason,
gcp_project_id: gcp_cluster.gcp_project_id,
zone: gcp_cluster.gcp_cluster_zone,
num_nodes: gcp_cluster.gcp_cluster_size,
machine_type: gcp_cluster.gcp_machine_type,
operation_id: gcp_cluster.gcp_operation_id,
endpoint: gcp_cluster.endpoint,
encrypted_access_token: gcp_cluster.encrypted_gcp_token,
encrypted_access_token_iv: gcp_cluster.encrypted_gcp_token_iv
},
platform_kubernetes_attributes: {
cluster_id: gcp_cluster.id,
api_url: api_url(gcp_cluster.endpoint),
ca_cert: gcp_cluster.ca_cert,
namespace: gcp_cluster.project_namespace,
username: gcp_cluster.username,
encrypted_password: gcp_cluster.encrypted_password,
encrypted_password_iv: gcp_cluster.encrypted_password_iv,
encrypted_token: gcp_cluster.encrypted_kubernetes_token,
encrypted_token_iv: gcp_cluster.encrypted_kubernetes_token_iv
} )
end
end
def down
execute('DELETE FROM clusters')
end
private
def api_url(endpoint)
endpoint ? 'https://' + endpoint : nil
end
end
......@@ -66,6 +66,7 @@ future GitLab releases.**
| **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name |
| **CI_PROJECT_PATH_SLUG** | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
| **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project |
| **CI_PROJECT_VISIBILITY** | 10.3 | all | The project visibility (internal, private, public) |
| **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry |
| **CI_REGISTRY_IMAGE** | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project |
| **CI_REGISTRY_PASSWORD** | 9.0 | all | The password to use to push containers to the GitLab Container Registry |
......
# System hooks
Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `project_rename`, `project_transfer`, `project_update`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`.
Your GitLab instance can perform HTTP POST requests on the following events:
- `project_create`
- `project_destroy`
- `project_rename`
- `project_transfer`
- `project_update`
- `user_add_to_team`
- `user_remove_from_team`
- `user_create`
- `user_destroy`
- `user_rename`
- `key_create`
- `key_destroy`
- `group_create`
- `group_destroy`
- `group_rename`
- `user_add_to_group`
- `user_remove_from_group`
The triggers for most of these are self-explanatory, but `project_update` and `project_rename` deserve some clarification: `project_update` is fired any time an attribute of a project is changed (name, description, tags, etc.) *unless* the `path` attribute is also changed. In that case, a `project_rename` is triggered instead (so that, for instance, if all you care about is the repo URL, you can just listen for `project_rename`).
......@@ -72,6 +90,9 @@ X-Gitlab-Event: System Hook
}
```
Note that `project_rename` is not triggered if the namespace changes.
Please refer to `group_rename` and `user_rename` for that case.
**Project transferred:**
```json
......@@ -175,6 +196,21 @@ X-Gitlab-Event: System Hook
}
```
**User renamed:**
```json
{
"event_name": "user_rename",
"created_at": "2017-11-01T11:21:04Z",
"updated_at": "2017-11-01T14:04:47Z",
"name": "new-name",
"email": "best-email@example.tld",
"user_id": 58,
"username": "new-exciting-name",
"old_username": "old-boring-name"
}
```
**Key added**
```json
......@@ -209,13 +245,15 @@ X-Gitlab-Event: System Hook
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "group_create",
"name": "StoreCloud",
"owner_email": "johnsmith@gmail.com",
"owner_name": "John Smith",
"owner_email": null,
"owner_name": null,
"path": "storecloud",
"group_id": 78
}
```
`owner_name` and `owner_email` are always `null`. Please see https://gitlab.com/gitlab-org/gitlab-ce/issues/39675.
**Group removed:**
```json
......@@ -224,13 +262,35 @@ X-Gitlab-Event: System Hook
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "group_destroy",
"name": "StoreCloud",
"owner_email": "johnsmith@gmail.com",
"owner_name": "John Smith",
"owner_email": null,
"owner_name": null,
"path": "storecloud",
"group_id": 78
}
```
`owner_name` and `owner_email` are always `null`. Please see https://gitlab.com/gitlab-org/gitlab-ce/issues/39675.
**Group renamed:**
```json
{
"event_name": "group_rename",
"created_at": "2017-10-30T15:09:00Z",
"updated_at": "2017-11-01T10:23:52Z",
"name": "Better Name",
"path": "better-name",
"full_path": "parent-group/better-name",
"group_id": 64,
"owner_name": null,
"owner_email": null,
"old_path": "old-name",
"old_full_path": "parent-group/old-name"
}
```
`owner_name` and `owner_email` are always `null`. Please see https://gitlab.com/gitlab-org/gitlab-ce/issues/39675.
**New Group Member:**
```json
......
......@@ -144,6 +144,12 @@ has a `.gitlab-ci.yml` or not:
All you need to do is remove your existing `.gitlab-ci.yml`, and you can even
do that in a branch to test Auto DevOps before committing to `master`.
NOTE: **Note:**
If you are a GitLab Administrator, you can enable Auto DevOps instance wide
in **Admin Area > Settings > Continuous Integration and Deployment**. Doing that,
all the projects that haven't explicitly set an option will have Auto DevOps
enabled by default.
## Stages of Auto DevOps
The following sections describe the stages of Auto DevOps. Read them carefully
......
......@@ -11,7 +11,7 @@ class Spinach::Features::ProfileNotifications < Spinach::FeatureSteps
end
step 'I select Mention setting from dropdown' do
first(:link, "On mention").trigger('click')
first(:link, "On mention").click
end
step 'I should see Notification saved message' do
......
......@@ -40,6 +40,7 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
step 'I submit new branch form with invalid name' do
fill_in 'branch_name', with: '1.0 stable'
page.find("body").click # defocus the branch_name input
select_branch('master')
click_button 'Create branch'
end
......@@ -70,17 +71,16 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
step "I click branch 'improve/awesome' delete link" do
page.within '.js-branch-improve\/awesome' do
find('.btn-remove').click
sleep 0.05
accept_alert { find('.btn-remove').click }
end
end
step "I should not see branch 'improve/awesome'" do
expect(page.all(visible: true)).not_to have_content 'improve/awesome'
expect(page).to have_css('.js-branch-improve\\/awesome', visible: :hidden)
end
def select_branch(branch_name)
click_button 'master'
find('.git-revision-dropdown-toggle').click
page.within '#new-branch-form .dropdown-menu' do
click_link branch_name
......
......@@ -16,7 +16,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
step 'I delete all labels' do
page.within '.labels' do
page.all('.remove-row').each do
first('.remove-row').click
accept_confirm { first('.remove-row').click }
end
end
end
......
......@@ -3,6 +3,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
include SharedProject
include SharedPaths
include SharedMarkdown
include CapybaraHelpers
step 'I should see milestone "v2.2"' do
milestone = @project.milestones.find_by(title: "v2.2")
......@@ -65,7 +66,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
end
step 'I click link to remove milestone' do
click_link 'Delete'
confirm_modal_if_present { click_link 'Delete' }
end
step 'I should see no milestones' do
......
......@@ -215,7 +215,7 @@ module SharedDiffNote
end
step 'I click side-by-side diff button' do
find('#parallel-diff-btn').trigger('click')
find('#parallel-diff-btn').click
end
step 'I see side-by-side diff button' do
......@@ -227,12 +227,11 @@ module SharedDiffNote
end
def click_diff_line(code)
find(".line_holder[id='#{code}'] td:nth-of-type(1)").trigger 'mouseover'
find(".line_holder[id='#{code}'] button").trigger 'click'
find(".line_holder[id='#{code}'] button").click
end
def click_parallel_diff_line(code, line_type)
find(".line_holder.parallel td[id='#{code}']").find(:xpath, 'preceding-sibling::*[1][self::td]').trigger 'mouseover'
find(".line_holder.parallel button[data-line-code='#{code}']").trigger 'click'
find(".line_holder.parallel td[id='#{code}']").find(:xpath, 'preceding-sibling::*[1][self::td]').hover
find(".line_holder.parallel button[data-line-code='#{code}']").click
end
end
......@@ -14,7 +14,7 @@ module SharedNote
find('.more-actions').click
find('.more-actions .dropdown-menu li', match: :first)
find(".js-note-delete").click
accept_confirm { find(".js-note-delete").click }
end
end
......
require 'capybara/poltergeist'
require 'capybara-screenshot/spinach'
# Give CI some extra time
timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30
Capybara.javascript_driver = :poltergeist
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(
app,
js_errors: true,
timeout: timeout,
window_size: [1366, 768],
url_whitelist: %w[localhost 127.0.0.1],
url_blacklist: %w[.mp4 .png .gif .avi .bmp .jpg .jpeg],
phantomjs_options: [
'--load-images=yes'
]
Capybara.javascript_driver = :chrome
Capybara.register_driver :chrome do |app|
extra_args = []
extra_args << 'headless' unless ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: {
'args' => %w[no-sandbox disable-gpu --window-size=1240,1400] + extra_args
}
)
Capybara::Selenium::Driver
.new(app, browser: :chrome, desired_capabilities: capabilities)
end
Capybara.default_max_wait_time = timeout
......@@ -24,6 +23,10 @@ Capybara.ignore_hidden_elements = false
# Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run
# From https://github.com/mattheworiordan/capybara-screenshot/issues/84#issuecomment-41219326
Capybara::Screenshot.register_driver(:chrome) do |driver, path|
driver.browser.save_screenshot(path)
end
Spinach.hooks.before_run do
TestEnv.eager_load_driver_server
......
module CapybaraHelpers
def confirm_modal_if_present
if Capybara.current_driver == Capybara.javascript_driver
accept_confirm { yield }
return
end
yield
end
end
......@@ -46,14 +46,14 @@ module Gitlab
# Returns the current real time in a given precision.
#
# Returns the time as a Float.
# Returns the time as a Fixnum.
def self.real_time(precision = :millisecond)
Process.clock_gettime(Process::CLOCK_REALTIME, precision)
end
# Returns the current monotonic clock time in a given precision.
#
# Returns the time as a Float.
# Returns the time as a Fixnum.
def self.monotonic_time(precision = :millisecond)
Process.clock_gettime(Process::CLOCK_MONOTONIC, precision)
end
......
......@@ -36,7 +36,7 @@ module Gitlab
end
def track_query(raw_query, bindings, start, finish)
duration = finish - start
duration = (finish - start) * 1000.0
query_info = { duration: duration.round(3), sql: raw_query }
PEEK_DB_CLIENT.query_details << query_info
......
......@@ -89,9 +89,11 @@ module Gitlab
ActiveSupport::Notifications.subscribe('sql.active_record') do |_, start, finish, _, data|
next unless same_thread?
unless data.fetch(:cached, data[:name] == 'CACHE')
track_query(data[:sql].strip, data[:binds], start, finish)
end
end
end
def subscribe_to_action_view
regex = /render_(template|partial)\.action_view/
......
......@@ -7,6 +7,7 @@ module Gitlab
class RequestBlockerMiddleware
@@num_active_requests = Concurrent::AtomicFixnum.new(0)
@@block_requests = Concurrent::AtomicBoolean.new(false)
@@slow_requests = Concurrent::AtomicBoolean.new(false)
# Returns the number of requests the server is currently processing.
def self.num_active_requests
......@@ -19,9 +20,15 @@ module Gitlab
@@block_requests.value = true
end
# Slows down incoming requests (useful for race conditions).
def self.slow_requests!
@@slow_requests.value = true
end
# Allows the server to accept requests again.
def self.allow_requests!
@@block_requests.value = false
@@slow_requests.value = false
end
def initialize(app)
......@@ -33,6 +40,7 @@ module Gitlab
if block_requests?
block_request(env)
else
sleep 0.2 if slow_requests?
@app.call(env)
end
ensure
......@@ -45,6 +53,10 @@ module Gitlab
@@block_requests.true?
end
def slow_requests?
@@slow_requests.true?
end
def block_request(env)
[503, {}, []]
end
......
# rubocop:disable Style/ClassVars
module Gitlab
module Testing
class RequestInspectorMiddleware
@@log_requests = Concurrent::AtomicBoolean.new(false)
@@logged_requests = Concurrent::Array.new
@@inject_headers = Concurrent::Hash.new
# Resets the current request log and starts logging requests
def self.log_requests!(headers = {})
@@inject_headers.replace(headers)
@@logged_requests.replace([])
@@log_requests.value = true
end
# Stops logging requests
def self.stop_logging!
@@log_requests.value = false
end
def self.requests
@@logged_requests
end
def initialize(app)
@app = app
end
def call(env)
return @app.call(env) unless @@log_requests.true?
url = env['REQUEST_URI']
env.merge! http_headers_env(@@inject_headers) if @@inject_headers.any?
request_headers = env_http_headers(env)
status, headers, body = @app.call(env)
request = OpenStruct.new(
url: url,
status_code: status,
request_headers: request_headers,
response_headers: headers
)
log_request request
[status, headers, body]
end
private
def env_http_headers(env)
Hash[*env.select { |k, v| k.start_with? 'HTTP_' }
.collect { |k, v| [k.sub(/^HTTP_/, ''), v] }
.collect { |k, v| [k.split('_').collect(&:capitalize).join('-'), v] }
.sort
.flatten]
end
def http_headers_env(headers)
Hash[*headers
.collect { |k, v| [k.split('-').collect(&:upcase).join('_'), v] }
.collect { |k, v| [k.prepend('HTTP_'), v] }
.flatten]
end
def log_request(response)
@@logged_requests.push(response)
end
end
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.
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