Commit 72157766 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'master' into sh-headless-chrome-support

* master: (33 commits)
  Ignore SQL CACHE hits in Sherlock
  Fix SQL timings for the performance bar
  Find the LFS-objects for a fork within a the fork network
  Remove bottom-border from last responsive table row
  Add system hooks user_rename and group_rename
  Unlink a project from a fork network when it's source was deleted.
  Make sure the settings page renders when root of a fork is deleted
  Remove Peek's original keyboard shortcut (numpad 0, keycode 96)
  Add application setting to Auto DevOps docs
  Enable MergeableSelector in scss-lint (for !14567)
  Enable MergeableSelector in scss-lint (for !14055)
  Enable MergeableSelector in scss-lint (for !14062)
  Enable MergeableSelector in scss-lint (for !14398)
  Enable MergeableSelector in scss-lint (for !13480)
  Enable MergeableSelector in scss-lint (for !13473)
  Enable MergeableSelector in scss-lint (for !13600)
  Enable MergeableSelector in scss-lint
  Resolve ""To do" should be "Todos" on Todos list page"
  Avoid regenerating the ref path for the environment
  Remove white space at bottom of issue boards
  ...
parents 817c7fb1 4841c3cb
......@@ -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
......
/* globals Flash */
import Visibility from 'visibilityjs';
import axios from 'axios';
import setAxiosCsrfToken from './lib/utils/axios_utils';
import Poll from './lib/utils/poll';
import { s__ } from './locale';
import initSettingsPanels from './settings_panels';
......@@ -17,6 +18,7 @@ import Flash from './flash';
class ClusterService {
constructor(options = {}) {
this.options = options;
setAxiosCsrfToken();
}
fetchData() {
return axios.get(this.options.endpoint);
......
import axios from 'axios';
import csrf from './csrf';
export default function setAxiosCsrfToken() {
axios.defaults.headers.common[csrf.headerKey] = csrf.token;
}
......@@ -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 {
margin-top: 0;
margin-bottom: 5px;
}
h4 {
margin-top: 0;
margin-bottom: 5px;
}
.bs-callout p:last-child {
margin-bottom: 0;
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 {
position: relative;
top: -2px;
margin-right: 3px;
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 {
background: $gl-success !important;
.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,15 +141,15 @@
svg {
fill: $gl-text-color-secondary;
}
}
.nav-item-name {
flex: 1;
}
.nav-item-name {
flex: 1;
}
li.active {
> a {
font-weight: $gl-font-weight-bold;
&.active {
> a {
font-weight: $gl-font-weight-bold;
}
}
}
......
......@@ -727,11 +727,11 @@
.pika-single.animate-picker.is-bound {
@include set-visible;
}
.pika-single.animate-picker.is-bound.is-hidden {
@include set-invisible;
overflow: 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,22 +165,36 @@
&:last-child {
border-right: none;
}
}
td.blame-commit {
padding: 5px 10px;
min-width: 400px;
max-width: 400px;
background: $gray-light;
border-left: 3px solid;
&.blame-commit {
padding: 5px 10px;
min-width: 400px;
max-width: 400px;
background: $gray-light;
border-left: 3px solid;
.commit-row-title {
display: flex;
}
.item-title {
flex: 1;
margin-right: 0.5em;
}
}
&.line-numbers {
float: none;
border-left: 1px solid $blame-line-numbers-border;
.commit-row-title {
display: flex;
i {
float: none;
margin-right: 0;
}
}
.item-title {
flex: 1;
margin-right: 0.5em;
&.lines {
padding: 0;
}
}
......@@ -195,20 +209,6 @@
border-left-color: mix($blame-gray, $blame-cyan, $i / 4.0 * 100%);
}
}
td.line-numbers {
float: none;
border-left: 1px solid $blame-line-numbers-border;
i {
float: none;
margin-right: 0;
}
}
td.lines {
padding: 0;
}
}
&.logs {
......
......@@ -463,10 +463,10 @@
word-break: break-all;
}
}
}
.filter-dropdown-item.droplab-item-active .btn {
@extend %filter-dropdown-item-btn-hover;
&.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 {
margin: 0;
.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;
}
.alert-warning + .alert-warning {
background-color: $orange-600;
border-color: $orange-600;
}
&:only-of-type {
background-color: $orange-500;
border-color: $orange-500;
}
.alert-warning + .alert-warning + .alert-warning {
background-color: $orange-700;
border-color: $orange-700;
}
+ .alert-warning {
background-color: $orange-600;
border-color: $orange-600;
.alert-warning + .alert-warning + .alert-warning + .alert-warning {
background-color: $orange-800;
border-color: $orange-800;
}
+ .alert-warning {
background-color: $orange-700;
border-color: $orange-700;
.alert-warning:only-of-type {
background-color: $orange-500;
border-color: $orange-500;
+ .alert-warning {
background-color: $orange-800;
border-color: $orange-800;
}
}
}
}
}
......
......@@ -299,40 +299,40 @@ ul.indent-list {
}
}
.group-list-tree .avatar-container.content-loading {
position: relative;
.group-list-tree {
.avatar-container.content-loading {
position: relative;
> a,
> a .avatar {
height: 100%;
border-radius: 50%;
}
> a,
> a .avatar {
height: 100%;
border-radius: 50%;
}
> a {
padding: 2px;
}
> a {
padding: 2px;
> a .avatar {
border: 2px solid $white-normal;
.avatar {
border: 2px solid $white-normal;
&.identicon {
line-height: 30px;
&.identicon {
line-height: 30px;
}
}
}
}
&::after {
content: "";
position: absolute;
height: 100%;
width: 100%;
background-color: transparent;
border: 2px outset $kdb-border;
border-radius: 50%;
animation: spin-avatar 3s infinite linear;
&::after {
content: "";
position: absolute;
height: 100%;
width: 100%;
background-color: transparent;
border: 2px outset $kdb-border;
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,7 +25,10 @@
margin: 0;
padding: $gl-padding 0;
border: none;
border-bottom: 1px solid $white-normal;
&:not(:last-child) {
border-bottom: 1px solid $white-normal;
}
}
}
......
......@@ -340,11 +340,64 @@
}
}
.project-item-select-holder.btn-group {
display: flex;
max-width: 350px;
overflow: hidden;
float: right;
.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;
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;
color: $gl-grayish-blue;
}
.select2-drop-mask {
z-index: 998;
}
.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,58 +134,46 @@
.select2-drop-auto-width & {
padding: 15px 15px 5px;
}
}
.select2-search input {
padding: 2px 25px 2px 5px;
background: $white-light image-url('select2.png');
background-repeat: no-repeat;
background-position: right 0 bottom 6px;
border: 1px solid $input-border;
border-radius: $border-radius-default;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
&:focus {
border-color: $input-border-focus;
input {
padding: 2px 25px 2px 5px;
background: $white-light image-url('select2.png');
background-repeat: no-repeat;
background-position: right 0 bottom 6px;
border: 1px solid $input-border;
border-radius: $border-radius-default;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
&:focus {
border-color: $input-border-focus;
}
&.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-search input.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;
}
.select2-result-label,
.select2-more-results {
padding: #{$gl-padding / 2} $gl-padding;
}
padding: 10px 0;
.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; }
......
......@@ -72,7 +72,7 @@
}
.boards-list {
height: calc(100vh - 152px);
height: calc(100vh - 105px);
width: 100%;
padding-top: 25px;
padding-bottom: 25px;
......@@ -81,8 +81,12 @@
overflow-x: scroll;
white-space: nowrap;
@media (min-width: $screen-sm-min) {
height: calc(100vh - 222px);
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
height: calc(100vh - 90px);
}
@media (min-width: $screen-md-min) {
height: calc(100vh - 160px);
min-height: 475px;
}
}
......
......@@ -68,18 +68,18 @@
&.affix {
top: $header-height;
}
// with sidebar
&.affix.sidebar-expanded {
right: 306px;
left: 16px;
}
// with sidebar
&.sidebar-expanded {
right: 306px;
left: 16px;
}
// without sidebar
&.affix.sidebar-collapsed {
right: 16px;
left: 16px;
// without sidebar
&.sidebar-collapsed {
right: 16px;
left: 16px;
}
}
&.affix-top {
......
......@@ -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,42 +269,51 @@
font-size: 12px;
}
.legend-axis-text {
fill: $black;
}
> svg {
position: absolute;
height: 100%;
width: 100%;
left: 0;
top: 0;
.tick {
> line {
stroke: $gray-darker;
.label-axis-text,
.text-metric-usage {
fill: $black;
font-weight: $gl-font-weight-normal;
font-size: 12px;
}
> text {
font-size: 12px;
.legend-axis-text {
fill: $black;
}
}
.text-metric-title {
font-size: 12px;
}
.tick > text {
font-size: 12px;
}
.y-label-text,
.x-label-text {
fill: $gray-darkest;
}
.text-metric-title {
font-size: 12px;
}
.axis-tick {
stroke: $gray-darker;
}
.y-label-text,
.x-label-text {
fill: $gray-darkest;
}
@media (max-width: $screen-sm-max) {
.label-axis-text,
.text-metric-usage,
.legend-axis-text {
font-size: 8px;
.axis-tick {
stroke: $gray-darker;
}
.tick > text {
font-size: 8px;
@media (max-width: $screen-sm-max) {
.label-axis-text,
.text-metric-usage,
.legend-axis-text {
font-size: 8px;
}
.tick > text {
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,35 +229,35 @@
margin: 0;
padding: 0;
height: 100%;
}
// Fixes footer container to bottom of viewport
.devise-layout-html body {
// offset height of fixed header + 1 to avoid scroll
height: calc(100% - 51px);
margin: 0;
padding: 0;
// Fixes footer container to bottom of viewport
body {
// offset height of fixed header + 1 to avoid scroll
height: calc(100% - 51px);
margin: 0;
padding: 0;
.page-wrap {
min-height: 100%;
position: relative;
}
.page-wrap {
min-height: 100%;
position: relative;
}
.footer-container,
hr.footer-fixed {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 40px;
background: $white-light;
}
.footer-container,
hr.footer-fixed {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 40px;
background: $white-light;
}
.navless-container {
padding: 65px 15px; // height of footer + bottom padding of email confirmation link
.navless-container {
padding: 65px 15px; // height of footer + bottom padding of email confirmation link
@media (max-width: $screen-xs-max) {
padding: 0 15px 65px;
@media (max-width: $screen-xs-max) {
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,16 +485,16 @@
padding-bottom: 0;
}
}
}
.mr-info-list.mr-memory-usage {
p {
float: left;
}
&.mr-memory-usage {
p {
float: left;
}
.memory-graph-container {
float: left;
margin-left: 5px;
.memory-graph-container {
float: left;
margin-left: 5px;
}
}
}
......
......@@ -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;
}
.milestone-buttons {
margin-left: auto;
}
.status-box {
order: 1;
}
.milestone-buttons {
margin-left: auto;
order: 2;
.verbose {
display: none;
}
}
.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,57 +312,72 @@ ul.notes {
}
}
.diff-file .notes_holder {
font-family: $regular_font;
.diff-file {
.is-over {
.add-diff-note {
display: inline-block;
}
}
td {
border: 1px solid $white-normal;
border-left: none;
// Merge request notes in diffs
// Diff is inline
.notes_content .note-header .note-headline-light {
display: inline-block;
position: relative;
}
&.notes_line {
vertical-align: middle;
text-align: center;
padding: 10px 0;
background: $gray-light;
color: $text-color;
}
.notes_holder {
font-family: $regular_font;
&.notes_line2 {
text-align: center;
padding: 10px 0;
border-left: 1px solid $note-line2-border !important;
}
td {
border: 1px solid $white-normal;
border-left: none;
&.notes_content {
background-color: $gray-light;
border-width: 1px 0;
padding: 0;
vertical-align: top;
white-space: normal;
&.notes_line {
vertical-align: middle;
text-align: center;
padding: 10px 0;
background: $gray-light;
color: $text-color;
}
&.parallel {
border-width: 1px;
&.notes_line2 {
text-align: center;
padding: 10px 0;
border-left: 1px solid $note-line2-border !important;
}
.discussion-notes {
&:not(:first-child) {
border-top: 1px solid $white-normal;
margin-top: 20px;
&.notes_content {
background-color: $gray-light;
border-width: 1px 0;
padding: 0;
vertical-align: top;
white-space: normal;
&.parallel {
border-width: 1px;
}
&:not(:last-child) {
border-bottom: 1px solid $white-normal;
margin-bottom: 20px;
.discussion-notes {
&:not(:first-child) {
border-top: 1px solid $white-normal;
margin-top: 20px;
}
&:not(:last-child) {
border-bottom: 1px solid $white-normal;
margin-bottom: 20px;
}
}
}
.notes {
background-color: $white-light;
}
.notes {
background-color: $white-light;
}
a code {
top: 0;
margin-right: 0;
a code {
top: 0;
margin-right: 0;
}
}
}
}
......@@ -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;
}
}
......@@ -499,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;
......@@ -662,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;
......@@ -711,20 +712,20 @@ ul.notes {
svg path {
fill: $gray-darkest;
}
}
.btn.discussion-create-issue-btn {
margin-left: -4px;
border-radius: 0;
border-right: 0;
&.discussion-create-issue-btn {
margin-left: -4px;
border-radius: 0;
border-right: 0;
a {
padding: 0;
line-height: 0;
a {
padding: 0;
line-height: 0;
&:hover {
text-decoration: none;
border: 0;
&:hover {
text-decoration: none;
border: 0;
}
}
}
}
......@@ -798,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,48 +469,48 @@
@extend .build-content:hover;
}
// Action Icons in big pipeline-graph nodes
.ci-action-icon-container.ci-action-icon-wrapper {
height: 30px;
width: 30px;
background: $white-light;
border: 1px solid $border-color;
border-radius: 100%;
display: block;
.ci-action-icon-container {
position: absolute;
right: 5px;
top: 5px;
&:hover {
background-color: $stage-hover-bg;
border: 1px solid $dropdown-toggle-active-border-color;
}
// Action Icons in big pipeline-graph nodes
&.ci-action-icon-wrapper {
height: 30px;
width: 30px;
background: $white-light;
border: 1px solid $border-color;
border-radius: 100%;
display: block;
svg {
fill: $gl-text-color-secondary;
position: relative;
left: 5px;
top: 2px;
width: 18px;
height: 18px;
}
&:hover {
background-color: $stage-hover-bg;
border: 1px solid $dropdown-toggle-active-border-color;
svg {
fill: $gl-text-color;
}
}
&.play {
svg {
width: #{$ci-action-icon-size - 8};
height: #{$ci-action-icon-size - 8};
left: 8px;
fill: $gl-text-color-secondary;
position: relative;
left: 5px;
top: 2px;
width: 18px;
height: 18px;
}
}
&:hover svg {
fill: $gl-text-color;
&.play {
svg {
width: #{$ci-action-icon-size - 8};
height: #{$ci-action-icon-size - 8};
left: 8px;
}
}
}
}
.ci-action-icon-container {
position: absolute;
right: 5px;
top: 5px;
}
.ci-status-icon svg {
height: 20px;
width: 20px;
......@@ -735,6 +754,28 @@ button.mini-pipeline-graph-dropdown-toggle {
left: -3px;
position: relative;
top: -2px;
&.icon-action-stop,
&.icon-action-cancel {
width: 12px;
height: 12px;
top: 1px;
left: -1px;
}
&.icon-action-play {
width: 11px;
height: 11px;
top: 1px;
left: 1px;
}
&.icon-action-retry {
width: 16px;
height: 16px;
top: 0;
left: -3px;
}
}
&:hover svg,
......@@ -751,27 +792,6 @@ button.mini-pipeline-graph-dropdown-toggle {
}
}
svg.icon-action-stop,
svg.icon-action-cancel {
width: 12px;
height: 12px;
top: 1px;
left: -1px;
}
svg.icon-action-play {
width: 11px;
height: 11px;
top: 1px;
left: 1px;
}
svg.icon-action-retry {
width: 16px;
height: 16px;
top: 0;
left: -3px;
}
}
......@@ -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 {
/**
* Top arrow in the dropdown in the big pipeline graph
*/
&::before,
&::after {
content: '';
......@@ -908,22 +925,23 @@ 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 {
transform: translate(-80%, 0);
min-width: 150px;
/**
* Center dropdown menu in mini graph
*/
&.dropdown-menu {
transform: translate(-80%, 0);
min-width: 150px;
@media(min-width: $screen-md-min) {
transform: translate(-50%, 0);
right: auto;
left: 50%;
min-width: 240px;
@media(min-width: $screen-md-min) {
transform: translate(-50%, 0);
right: auto;
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,35 +779,35 @@ a.deploy-project-label {
.nav {
padding-top: 12px;
padding-bottom: 12px;
}
.nav > li {
display: inline-block;
> li {
display: inline-block;
&:not(:last-child) {
margin-right: $gl-padding;
}
&:not(:last-child) {
margin-right: $gl-padding;
}
&.right {
vertical-align: top;
margin-top: 0;
&.right {
vertical-align: top;
margin-top: 0;
@media (min-width: $screen-lg-min) {
float: right;
@media (min-width: $screen-lg-min) {
float: right;
}
}
}
}
.nav > li > a {
padding: 0;
background-color: transparent;
font-size: 14px;
line-height: 29px;
color: $notes-light-color;
> a {
padding: 0;
background-color: transparent;
font-size: 14px;
line-height: 29px;
color: $notes-light-color;
&:hover,
&:focus {
color: $gl-text-color;
&:hover,
&:focus {
color: $gl-text-color;
}
}
}
}
......@@ -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 {
border-top: 1px solid $border-color;
/* Ensure we don't add border if there's only single li */
+ li {
border-top: 1px solid $border-color;
}
}
}
}
......@@ -5,10 +5,10 @@ table .sherlock-code {
.sherlock-code {
pre {
word-wrap: normal;
}
pre code {
white-space: pre;
code {
white-space: pre;
}
}
}
......@@ -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,16 +40,16 @@
@media (max-width: $screen-xs-max) {
width: 100%;
}
}
.person .spark {
display: block;
background: $stat-graph-common-bg;
width: 100%;
}
.spark {
display: block;
background: $stat-graph-common-bg;
width: 100%;
}
.person .area-contributor {
fill: $stat-graph-orange-fill;
.area-contributor {
fill: $stat-graph-orange-fill;
}
}
}
......
......@@ -161,10 +161,10 @@ ul.wiki-pages-list.content-list {
list-style: none;
margin-left: 0;
padding-left: 15px;
}
ul li {
padding: 5px 0;
li {
padding: 5px 0;
}
}
}
......
......@@ -19,10 +19,12 @@ class Admin::ApplicationsController < Admin::ApplicationController
end
def create
@application = Doorkeeper::Application.new(application_params)
@application = Applications::CreateService.new(current_user, application_params).execute(request)
if @application.save
redirect_to_admin_page
if @application.persisted?
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to admin_application_url(@application)
else
render :new
end
......@@ -41,13 +43,6 @@ class Admin::ApplicationsController < Admin::ApplicationController
redirect_to admin_applications_url, status: 302, notice: 'Application was successfully destroyed.'
end
protected
def redirect_to_admin_page
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to admin_application_url(@application)
end
private
def set_application
......
......@@ -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
......
......@@ -16,25 +16,18 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
end
def create
@application = Doorkeeper::Application.new(application_params)
@application = Applications::CreateService.new(current_user, create_application_params).execute(request)
@application.owner = current_user
if @application.persisted?
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
if @application.save
redirect_to_oauth_application_page
redirect_to oauth_application_url(@application)
else
set_index_vars
render :index
end
end
protected
def redirect_to_oauth_application_page
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to oauth_application_url(@application)
end
private
def verify_user_oauth_applications_enabled
......@@ -61,4 +54,10 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
rescue_from ActiveRecord::RecordNotFound do |exception|
render "errors/not_found", layout: "errors", status: 404
end
def create_application_params
application_params.tap do |params|
params[:owner] = current_user
end
end
end
......@@ -11,10 +11,10 @@ class Profiles::KeysController < Profiles::ApplicationController
end
def create
@key = Keys::CreateService.new(current_user, key_params).execute
@key = Keys::CreateService.new(current_user, key_params.merge(ip_address: request.remote_ip)).execute
if @key.persisted?
redirect_to_profile_key_path
redirect_to profile_key_path(@key)
else
@keys = current_user.keys.select(&:persisted?)
render :index
......@@ -50,12 +50,6 @@ class Profiles::KeysController < Profiles::ApplicationController
end
end
protected
def redirect_to_profile_key_path
redirect_to profile_key_path(@key)
end
private
def key_params
......
......@@ -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
......
......@@ -110,7 +110,7 @@ class Environment < ActiveRecord::Base
end
def ref_path
"refs/#{Repository::REF_ENVIRONMENTS}/#{generate_slug}"
"refs/#{Repository::REF_ENVIRONMENTS}/#{slug}"
end
def formatted_external_url
......@@ -164,6 +164,10 @@ class Environment < ActiveRecord::Base
end
end
def slug
super.presence || generate_slug
end
# An environment name is not necessarily suitable for use in URLs, DNS
# or other third-party contexts, so provide a slugified version. A slug has
# the following properties:
......
# Placeholder class for model that is implemented in EE
# It will reserve (ee#3853) '&' as a reference prefix, but the table does not exists in CE
class Epic < ActiveRecord::Base
prepend EE::Epic
# TODO: this will be implemented as part of #3853
def to_reference
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?
......
......@@ -1040,6 +1040,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
......
......@@ -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)
......
module Applications
class CreateService
def initialize(current_user, params)
@current_user = current_user
@params = params
@ip_address = @params.delete(:ip_address)
end
def execute(request = nil)
Doorkeeper::Application.create(@params)
end
end
end
......@@ -4,6 +4,7 @@ module Keys
def initialize(user, params)
@user, @params = user, params
@ip_address = @params.delete(:ip_address)
end
def notification_service
......
......@@ -3,18 +3,24 @@ module Projects
def execute
return unless @project.forked?
@project.forked_from_project.lfs_objects.find_each do |lfs_object|
lfs_object.projects << @project
if fork_source = @project.fork_source
fork_source.lfs_objects.find_each do |lfs_object|
lfs_object.projects << @project
end
refresh_forks_count(fork_source)
end
merge_requests = @project.forked_from_project.merge_requests.opened.from_project(@project)
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))
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
)
if event == :rename
data.merge!(
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
......@@ -8,7 +8,7 @@
%li.todos-pending{ class: active_when(params[:state].blank? || params[:state] == 'pending') }>
= link_to todos_filter_path(state: 'pending') do
%span
To do
Todos
%span.badge
= number_with_delimiter(todos_pending_count)
%li.todos-done{ class: active_when(params[:state] == 'done') }>
......
- 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
......
......@@ -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: 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: Todos spelled correctly on Todos list page
merge_request: 15015
author:
type: changed
---
title: Fix editing issue description in mobile view
merge_request:
author:
type: fixed
---
title: Fix bitbucket login
merge_request: 15051
author:
type: fixed
---
title: Adds callback functions for initial request in clusters page
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: Normalize LDAP DN when looking up identity
merge_request:
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: Fix missing Import/Export issue assignees
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: Allow boards as top level route
title: Avoid regenerating the ref path for the environment
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
......@@ -501,7 +501,7 @@ production: &base
# Gitaly settings
gitaly:
# Path to the directory containing Gitaly client executables.
client_path: /home/git/gitaly
client_path: /home/git/gitaly/bin
# Default Gitaly authentication token. Can be overriden per storage. Can
# be left blank when Gitaly is running locally on a Unix socket, which
# is the normal way to deploy Gitaly.
......
# 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
......
......@@ -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,7 +89,9 @@ module Gitlab
ActiveSupport::Notifications.subscribe('sql.active_record') do |_, start, finish, _, data|
next unless same_thread?
track_query(data[:sql].strip, data[:binds], start, finish)
unless data.fetch(:cached, data[:name] == 'CACHE')
track_query(data[:sql].strip, data[:binds], start, finish)
end
end
end
......
require 'spec_helper'
describe LfsRequest do
include ProjectForksHelper
controller(Projects::GitHttpClientController) do
# `described_class` is not available in this context
include LfsRequest # rubocop:disable RSpec/DescribedClass
def show
storage_project
render nothing: true
end
def project
@project ||= Project.find(params[:id])
end
def download_request?
true
end
def ci?
false
end
end
let(:project) { create(:project, :public) }
before do
stub_lfs_setting(enabled: true)
end
describe '#storage_project' do
it 'assigns the project as storage project' do
get :show, id: project.id
expect(assigns(:storage_project)).to eq(project)
end
it 'assigns the source of a forked project' do
forked_project = fork_project(project)
get :show, id: forked_project.id
expect(assigns(:storage_project)).to eq(project)
end
end
end
......@@ -52,7 +52,7 @@ feature 'Dashboard Todos' do
end
it 'updates todo count' do
expect(page).to have_content 'To do 0'
expect(page).to have_content 'Todos 0'
expect(page).to have_content 'Done 1'
end
......@@ -81,7 +81,7 @@ feature 'Dashboard Todos' do
end
it 'updates todo count' do
expect(page).to have_content 'To do 1'
expect(page).to have_content 'Todos 1'
expect(page).to have_content 'Done 0'
end
end
......@@ -200,7 +200,7 @@ feature 'Dashboard Todos' do
end
it 'updates todo count' do
expect(page).to have_content 'To do 1'
expect(page).to have_content 'Todos 1'
expect(page).to have_content 'Done 0'
end
end
......@@ -256,7 +256,7 @@ feature 'Dashboard Todos' do
end
it 'shows "All done" message!' do
expect(page).to have_content 'To do 0'
expect(page).to have_content 'Todos 0'
expect(page).to have_content "You're all done!"
expect(page).not_to have_selector('.gl-pagination')
end
......@@ -283,7 +283,7 @@ feature 'Dashboard Todos' do
it 'updates todo count' do
mark_all_and_undo
expect(page).to have_content 'To do 2'
expect(page).to have_content 'Todos 2'
expect(page).to have_content 'Done 0'
end
......
require 'spec_helper'
feature 'Settings for a forked project', :js do
include ProjectForksHelper
let(:user) { create(:user) }
let(:original_project) { create(:project) }
let(:forked_project) { fork_project(original_project, user) }
before do
original_project.add_master(user)
forked_project.add_master(user)
sign_in(user)
end
shared_examples 'project settings for a forked projects' do
it 'allows deleting the link to the forked project' do
visit edit_project_path(forked_project)
click_button 'Remove fork relationship'
wait_for_requests
fill_in('confirm_name_input', with: forked_project.name)
click_button('Confirm')
expect(page).to have_content('The fork relationship has been removed.')
expect(forked_project.reload.forked?).to be_falsy
end
end
it_behaves_like 'project settings for a forked projects'
context 'when the original project is deleted' do
before do
original_project.destroy!
end
it_behaves_like 'project settings for a forked projects'
end
end
......@@ -134,6 +134,7 @@ describe Group, 'Routable' do
context 'with RequestStore active', :request_store do
it 'does not load the route table more than once' do
group.expires_full_path_cache
expect(group).to receive(:uncached_full_path).once.and_call_original
3.times { group.full_path }
......
......@@ -547,6 +547,15 @@ describe Environment do
expect(environment.slug).to eq(original_slug)
end
it "regenerates the slug if nil" do
environment = build(:environment, slug: nil)
new_slug = environment.slug
expect(new_slug).not_to be_nil
expect(environment.slug).to eq(new_slug)
end
end
describe '#generate_slug' do
......@@ -583,6 +592,12 @@ describe Environment do
it 'returns a path that uses the slug and does not have spaces' do
expect(environment.ref_path).to start_with('refs/environments/staging-review-1-')
end
it "doesn't change when the slug is nil initially" do
environment.slug = nil
expect(environment.ref_path).to eq(environment.ref_path)
end
end
describe '#external_url_for' do
......
......@@ -24,6 +24,16 @@ describe ForkNetwork do
end
end
describe '#merge_requests' do
it 'finds merge requests within the fork network' do
project = create(:project)
forked_project = fork_project(project)
merge_request = create(:merge_request, source_project: forked_project, target_project: project)
expect(project.fork_network.merge_requests).to include(merge_request)
end
end
context 'for a deleted project' do
it 'keeps the fork network' do
project = create(:project, :public)
......
......@@ -488,6 +488,47 @@ describe Group do
end
end
describe '#path_changed_hook' do
let(:system_hook_service) { SystemHooksService.new }
context 'for a new group' do
let(:group) { build(:group) }
before do
expect(group).to receive(:system_hook_service).and_return(system_hook_service)
end
it 'does not trigger system hook' do
expect(system_hook_service).to receive(:execute_hooks_for).with(group, :create)
group.save!
end
end
context 'for an existing group' do
let(:group) { create(:group, path: 'old-path') }
context 'when the path is changed' do
let(:new_path) { 'very-new-path' }
it 'triggers the rename system hook' do
expect(group).to receive(:system_hook_service).and_return(system_hook_service)
expect(system_hook_service).to receive(:execute_hooks_for).with(group, :rename)
group.update_attributes!(path: new_path)
end
end
context 'when the path is not changed' do
it 'does not trigger system hook' do
expect(group).not_to receive(:system_hook_service)
group.update_attributes!(name: 'new name')
end
end
end
end
describe '#secret_variables_for' do
let(:project) { create(:project, group: group) }
......
......@@ -1923,6 +1923,20 @@ describe Project do
expect(forked_project.in_fork_network_of?(other_project)).to be_falsy
end
end
describe '#fork_source' do
let!(:second_fork) { fork_project(forked_project) }
it 'returns the direct source if it exists' do
expect(second_fork.fork_source).to eq(forked_project)
end
it 'returns the root of the fork network when the directs source was deleted' do
forked_project.destroy
expect(second_fork.fork_source).to eq(project)
end
end
end
describe '#pushes_since_gc' do
......
......@@ -2217,6 +2217,42 @@ describe User do
end
end
describe '#username_changed_hook' do
context 'for a new user' do
let(:user) { build(:user) }
it 'does not trigger system hook' do
expect(user).not_to receive(:system_hook_service)
user.save!
end
end
context 'for an existing user' do
let(:user) { create(:user, username: 'old-username') }
context 'when the username is changed' do
let(:new_username) { 'very-new-name' }
it 'triggers the rename system hook' do
system_hook_service = SystemHooksService.new
expect(system_hook_service).to receive(:execute_hooks_for).with(user, :rename)
expect(user).to receive(:system_hook_service).and_return(system_hook_service)
user.update_attributes!(username: new_username)
end
end
context 'when the username is not changed' do
it 'does not trigger system hook' do
expect(user).not_to receive(:system_hook_service)
user.update_attributes!(email: 'asdf@asdf.com')
end
end
end
end
describe '#sync_attribute?' do
let(:user) { described_class.new }
......
require 'spec_helper'
describe ::Applications::CreateService do
let(:user) { create(:user) }
let(:params) { attributes_for(:application) }
let(:request) { ActionController::TestRequest.new(remote_ip: '127.0.0.1') }
subject { described_class.new(user, params) }
it 'creates an application' do
expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1)
end
end
......@@ -12,6 +12,9 @@ describe Projects::UnlinkForkService do
context 'with opened merge request on the source project' do
let(:merge_request) { create(:merge_request, source_project: forked_project, target_project: fork_link.forked_from_project) }
let(:merge_request2) { create(:merge_request, source_project: forked_project, target_project: fork_project(project)) }
let(:merge_request_in_fork) { create(:merge_request, source_project: forked_project, target_project: forked_project) }
let(:mr_close_service) { MergeRequests::CloseService.new(forked_project, user) }
before do
......@@ -22,9 +25,14 @@ describe Projects::UnlinkForkService do
it 'close all pending merge requests' do
expect(mr_close_service).to receive(:execute).with(merge_request)
expect(mr_close_service).to receive(:execute).with(merge_request2)
subject.execute
end
it 'does not close merge requests for the project being unlinked' do
expect(mr_close_service).not_to receive(:execute).with(merge_request_in_fork)
end
end
it 'remove fork relation' do
......@@ -53,4 +61,14 @@ describe Projects::UnlinkForkService do
expect(source.forks_count).to be_zero
end
context 'when the original project was deleted' do
it 'does not fail when the original project is deleted' do
source = forked_project.forked_from_project
source.destroy
forked_project.reload
expect { subject.execute }.not_to raise_error
end
end
end
......@@ -69,11 +69,48 @@ describe SystemHooksService do
expect(data[:project_visibility]).to eq('private')
end
context 'group_rename' do
it 'contains old and new path' do
allow(group).to receive(:path_was).and_return('old-path')
data = event_data(group, :rename)
expect(data).to include(:event_name, :name, :created_at, :updated_at, :full_path, :path, :group_id, :old_path, :old_full_path)
expect(data[:path]).to eq(group.path)
expect(data[:full_path]).to eq(group.path)
expect(data[:old_path]).to eq(group.path_was)
expect(data[:old_full_path]).to eq(group.path_was)
end
it 'contains old and new full_path for subgroup' do
subgroup = create(:group, parent: group)
allow(subgroup).to receive(:path_was).and_return('old-path')
data = event_data(subgroup, :rename)
expect(data[:full_path]).to eq(subgroup.full_path)
expect(data[:old_path]).to eq('old-path')
end
end
context 'user_rename' do
it 'contains old and new username' do
allow(user).to receive(:username_was).and_return('old-username')
data = event_data(user, :rename)
expect(data).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username, :old_username)
expect(data[:username]).to eq(user.username)
expect(data[:old_username]).to eq(user.username_was)
end
end
end
context 'event names' do
it { expect(event_name(user, :create)).to eq "user_create" }
it { expect(event_name(user, :destroy)).to eq "user_destroy" }
it { expect(event_name(user, :rename)).to eq 'user_rename' }
it { expect(event_name(project, :create)).to eq "project_create" }
it { expect(event_name(project, :destroy)).to eq "project_destroy" }
it { expect(event_name(project, :rename)).to eq "project_rename" }
......@@ -85,6 +122,7 @@ describe SystemHooksService do
it { expect(event_name(key, :destroy)).to eq 'key_destroy' }
it { expect(event_name(group, :create)).to eq 'group_create' }
it { expect(event_name(group, :destroy)).to eq 'group_destroy' }
it { expect(event_name(group, :rename)).to eq 'group_rename' }
it { expect(event_name(group_member, :create)).to eq 'user_add_to_group' }
it { expect(event_name(group_member, :destroy)).to eq 'user_remove_from_group' }
end
......
......@@ -38,6 +38,10 @@ module StubConfiguration
allow(Gitlab.config.backup).to receive_messages(to_settings(messages))
end
def stub_lfs_setting(messages)
allow(Gitlab.config.lfs).to receive_messages(to_settings(messages))
end
def stub_storage_settings(messages)
# Default storage is always required
messages['default'] ||= Gitlab.config.repositories.storages.default
......
/*
* This is a modified version of https://github.com/peek/peek/blob/master/app/assets/javascripts/peek.js
*
* - Removed the dependency on jquery.tipsy
* - Removed the initializeTipsy and toggleBar functions
* - Customized updatePerformanceBar to handle SQL queries report specificities
* - Changed /peek/results to /-/peek/results
* - Removed the keypress, pjax:end, page:change, and turbolinks:load handlers
*/
(function($) {
var fetchRequestResults, getRequestId, peekEnabled, toggleBar, updatePerformanceBar;
var fetchRequestResults, getRequestId, peekEnabled, updatePerformanceBar;
getRequestId = function() {
return $('#peek').data('request-id');
};
......@@ -41,22 +50,6 @@
});
return $(document).trigger('peek:render', [getRequestId(), results]);
};
toggleBar = function(event) {
var wrapper;
if ($(event.target).is(':input')) {
return;
}
if (event.which === 96 && !event.metaKey) {
wrapper = $('#peek');
if (wrapper.hasClass('disabled')) {
wrapper.removeClass('disabled');
return document.cookie = "peek=true; path=/";
} else {
wrapper.addClass('disabled');
return document.cookie = "peek=false; path=/";
}
}
};
fetchRequestResults = function() {
return $.ajax('/-/peek/results', {
data: {
......@@ -68,7 +61,6 @@
error: function(xhr, textStatus, error) {}
});
};
$(document).on('keypress', toggleBar);
$(document).on('peek:update', fetchRequestResults);
return $(function() {
if (peekEnabled()) {
......
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