Merge branch 'master' into sh-add-object-storage-qa
This commit is contained in:
commit
3c633844d0
|
@ -4,7 +4,9 @@ entry.
|
|||
|
||||
## 11.2.3 (2018-08-28)
|
||||
|
||||
- No changes.
|
||||
### Fixed (1 change)
|
||||
|
||||
- Fixed cache invalidation issue with diff lines from 11.2.2.
|
||||
|
||||
## 11.2.2 (2018-08-27)
|
||||
|
||||
|
@ -269,7 +271,9 @@ entry.
|
|||
|
||||
## 11.1.6 (2018-08-28)
|
||||
|
||||
- No changes.
|
||||
### Fixed (1 change)
|
||||
|
||||
- Fixed cache invalidation issue with diff lines from 11.2.2.
|
||||
|
||||
## 11.1.5 (2018-08-27)
|
||||
|
||||
|
|
5
Gemfile
5
Gemfile
|
@ -195,6 +195,9 @@ gem 're2', '~> 1.1.1'
|
|||
|
||||
gem 'version_sorter', '~> 2.1.0'
|
||||
|
||||
# Export Ruby Regex to Javascript
|
||||
gem 'js_regex', '~> 2.2.1'
|
||||
|
||||
# User agent parsing
|
||||
gem 'device_detector'
|
||||
|
||||
|
@ -365,7 +368,7 @@ group :development, :test do
|
|||
|
||||
gem 'benchmark-ips', '~> 2.3.0', require: false
|
||||
|
||||
gem 'license_finder', '~> 3.1', require: false
|
||||
gem 'license_finder', '~> 5.4', require: false
|
||||
gem 'knapsack', '~> 1.16'
|
||||
|
||||
gem 'activerecord_sane_schema_dumper', gem_versions['activerecord_sane_schema_dumper']
|
||||
|
|
23
Gemfile.lock
23
Gemfile.lock
|
@ -86,7 +86,6 @@ GEM
|
|||
bindata (2.4.3)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
blankslate (2.1.2.4)
|
||||
bootsnap (1.3.1)
|
||||
msgpack (~> 1.0)
|
||||
bootstrap_form (2.7.0)
|
||||
|
@ -428,6 +427,8 @@ GEM
|
|||
multipart-post
|
||||
oauth (~> 0.5, >= 0.5.0)
|
||||
jquery-atwho-rails (1.3.2)
|
||||
js_regex (2.2.1)
|
||||
regexp_parser (>= 0.4.11, <= 0.5.0)
|
||||
json (1.8.6)
|
||||
json-jwt (1.9.4)
|
||||
activesupport
|
||||
|
@ -463,13 +464,12 @@ GEM
|
|||
actionmailer (>= 3.2)
|
||||
letter_opener (~> 1.0)
|
||||
railties (>= 3.2)
|
||||
license_finder (3.1.1)
|
||||
license_finder (5.4.0)
|
||||
bundler
|
||||
httparty
|
||||
rubyzip
|
||||
thor
|
||||
toml (= 0.1.2)
|
||||
with_env (> 1.0)
|
||||
toml (= 0.2.0)
|
||||
with_env (= 1.1.0)
|
||||
xml-simple
|
||||
licensee (8.9.2)
|
||||
rugged (~> 0.24)
|
||||
|
@ -587,8 +587,7 @@ GEM
|
|||
parallel (1.12.1)
|
||||
parser (2.5.1.0)
|
||||
ast (~> 2.4.0)
|
||||
parslet (1.5.0)
|
||||
blankslate (~> 2.0)
|
||||
parslet (1.8.2)
|
||||
path_expander (1.0.2)
|
||||
peek (1.0.1)
|
||||
concurrent-ruby (>= 0.9.0)
|
||||
|
@ -726,6 +725,7 @@ GEM
|
|||
redis-store (>= 1.2, < 2)
|
||||
redis-store (1.4.1)
|
||||
redis (>= 2.2, < 5)
|
||||
regexp_parser (0.5.0)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
|
@ -907,8 +907,8 @@ GEM
|
|||
tilt (2.0.8)
|
||||
timecop (0.8.1)
|
||||
timfel-krb5-auth (0.8.3)
|
||||
toml (0.1.2)
|
||||
parslet (~> 1.5.0)
|
||||
toml (0.2.0)
|
||||
parslet (~> 1.8.0)
|
||||
toml-rb (1.0.0)
|
||||
citrus (~> 3.0, > 3.0)
|
||||
trollop (2.1.3)
|
||||
|
@ -1074,13 +1074,14 @@ DEPENDENCIES
|
|||
influxdb (~> 0.2)
|
||||
jira-ruby (~> 1.4)
|
||||
jquery-atwho-rails (~> 1.3.2)
|
||||
js_regex (~> 2.2.1)
|
||||
json-schema (~> 2.8.0)
|
||||
jwt (~> 1.5.6)
|
||||
kaminari (~> 1.0)
|
||||
knapsack (~> 1.16)
|
||||
kubeclient (~> 3.1.0)
|
||||
letter_opener_web (~> 1.3.0)
|
||||
license_finder (~> 3.1)
|
||||
license_finder (~> 5.4)
|
||||
licensee (~> 8.9)
|
||||
lograge (~> 0.5)
|
||||
loofah (~> 2.2)
|
||||
|
@ -1201,4 +1202,4 @@ DEPENDENCIES
|
|||
wikicloth (= 0.8.1)
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.3
|
||||
1.16.4
|
||||
|
|
|
@ -89,7 +89,6 @@ GEM
|
|||
bindata (2.4.3)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
blankslate (2.1.2.4)
|
||||
bootsnap (1.3.1)
|
||||
msgpack (~> 1.0)
|
||||
bootstrap_form (2.7.0)
|
||||
|
@ -431,6 +430,8 @@ GEM
|
|||
multipart-post
|
||||
oauth (~> 0.5, >= 0.5.0)
|
||||
jquery-atwho-rails (1.3.2)
|
||||
js_regex (2.2.1)
|
||||
regexp_parser (>= 0.4.11, <= 0.5.0)
|
||||
json (1.8.6)
|
||||
json-jwt (1.9.4)
|
||||
activesupport
|
||||
|
@ -466,13 +467,12 @@ GEM
|
|||
actionmailer (>= 3.2)
|
||||
letter_opener (~> 1.0)
|
||||
railties (>= 3.2)
|
||||
license_finder (3.1.1)
|
||||
license_finder (5.4.0)
|
||||
bundler
|
||||
httparty
|
||||
rubyzip
|
||||
thor
|
||||
toml (= 0.1.2)
|
||||
with_env (> 1.0)
|
||||
toml (= 0.2.0)
|
||||
with_env (= 1.1.0)
|
||||
xml-simple
|
||||
licensee (8.9.2)
|
||||
rugged (~> 0.24)
|
||||
|
@ -591,8 +591,7 @@ GEM
|
|||
parallel (1.12.1)
|
||||
parser (2.5.1.0)
|
||||
ast (~> 2.4.0)
|
||||
parslet (1.5.0)
|
||||
blankslate (~> 2.0)
|
||||
parslet (1.8.2)
|
||||
path_expander (1.0.2)
|
||||
peek (1.0.1)
|
||||
concurrent-ruby (>= 0.9.0)
|
||||
|
@ -735,6 +734,7 @@ GEM
|
|||
redis-store (>= 1.2, < 2)
|
||||
redis-store (1.4.1)
|
||||
redis (>= 2.2, < 5)
|
||||
regexp_parser (0.5.0)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
|
@ -914,8 +914,8 @@ GEM
|
|||
tilt (2.0.8)
|
||||
timecop (0.8.1)
|
||||
timfel-krb5-auth (0.8.3)
|
||||
toml (0.1.2)
|
||||
parslet (~> 1.5.0)
|
||||
toml (0.2.0)
|
||||
parslet (~> 1.8.0)
|
||||
toml-rb (1.0.0)
|
||||
citrus (~> 3.0, > 3.0)
|
||||
trollop (2.1.3)
|
||||
|
@ -1084,13 +1084,14 @@ DEPENDENCIES
|
|||
influxdb (~> 0.2)
|
||||
jira-ruby (~> 1.4)
|
||||
jquery-atwho-rails (~> 1.3.2)
|
||||
js_regex (~> 2.2.1)
|
||||
json-schema (~> 2.8.0)
|
||||
jwt (~> 1.5.6)
|
||||
kaminari (~> 1.0)
|
||||
knapsack (~> 1.16)
|
||||
kubeclient (~> 3.1.0)
|
||||
letter_opener_web (~> 1.3.0)
|
||||
license_finder (~> 3.1)
|
||||
license_finder (~> 5.4)
|
||||
licensee (~> 8.9)
|
||||
lograge (~> 0.5)
|
||||
loofah (~> 2.2)
|
||||
|
@ -1211,4 +1212,4 @@ DEPENDENCIES
|
|||
wikicloth (= 0.8.1)
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.3
|
||||
1.16.4
|
||||
|
|
|
@ -10,6 +10,7 @@ const hideFlash = (flashEl, fadeTransition = true) => {
|
|||
|
||||
flashEl.addEventListener('transitionend', () => {
|
||||
flashEl.remove();
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
if (document.body.classList.contains('flash-shown')) document.body.classList.remove('flash-shown');
|
||||
}, {
|
||||
once: true,
|
||||
|
|
|
@ -78,13 +78,13 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<article class="ide">
|
||||
<article class="ide position-relative d-flex flex-column align-items-stretch">
|
||||
<error-message
|
||||
v-if="errorMessage"
|
||||
:message="errorMessage"
|
||||
/>
|
||||
<div
|
||||
class="ide-view"
|
||||
class="ide-view flex-grow d-flex"
|
||||
>
|
||||
<find-file
|
||||
v-show="fileFindVisible"
|
||||
|
|
|
@ -24,12 +24,6 @@ export default {
|
|||
default: null,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.fileUpload.addEventListener('change', this.openFile);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$refs.fileUpload.removeEventListener('change', this.openFile);
|
||||
},
|
||||
methods: {
|
||||
createFile(target, file, isText) {
|
||||
const { name } = file;
|
||||
|
@ -85,6 +79,8 @@ export default {
|
|||
ref="fileUpload"
|
||||
type="file"
|
||||
class="hidden"
|
||||
multiple
|
||||
@change="openFile"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -95,16 +95,18 @@ export default {
|
|||
return this.file.changed || this.file.tempFile || this.file.staged;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'file.active': function fileActiveWatch(active) {
|
||||
if (this.file.type === 'blob' && active) {
|
||||
this.scrollIntoView();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.hasPathAtCurrentRoute()) {
|
||||
this.scrollIntoView(true);
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
if (this.file.type === 'blob' && this.file.active) {
|
||||
this.scrollIntoView();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['toggleTreeOpen']),
|
||||
clickFile() {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { __ } from '~/locale';
|
||||
|
||||
export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE = __('Regex pattern');
|
||||
export const PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE = __('To define internal users, first enable new users set to external');
|
||||
|
||||
function setUserInternalRegexPlaceholder(checkbox) {
|
||||
const userInternalRegex = document.getElementById('application_setting_user_default_internal_regex');
|
||||
if (checkbox && userInternalRegex) {
|
||||
if (checkbox.checked) {
|
||||
userInternalRegex.readOnly = false;
|
||||
userInternalRegex.placeholder = PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE;
|
||||
} else {
|
||||
userInternalRegex.readOnly = true;
|
||||
userInternalRegex.placeholder = PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function initUserInternalRegexPlaceholder() {
|
||||
const checkbox = document.getElementById('application_setting_user_default_external');
|
||||
setUserInternalRegexPlaceholder(checkbox);
|
||||
checkbox.addEventListener('change', () => {
|
||||
setUserInternalRegexPlaceholder(checkbox);
|
||||
});
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
import initAdmin from './admin';
|
||||
import initUserInternalRegexPlaceholder from './application_settings/account_and_limits';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initAdmin);
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initAdmin();
|
||||
initUserInternalRegexPlaceholder();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
export default class UserInternalRegexHandler {
|
||||
constructor() {
|
||||
this.regexPattern = $('[data-user-internal-regex-pattern]').data('user-internal-regex-pattern');
|
||||
if (this.regexPattern && this.regexPattern !== '') {
|
||||
this.regexOptions = $('[data-user-internal-regex-options]').data('user-internal-regex-options');
|
||||
this.external = $('#user_external');
|
||||
this.warningMessage = $('#warning_external_automatically_set');
|
||||
this.addListenerToEmailField();
|
||||
this.addListenerToUserExternalCheckbox();
|
||||
}
|
||||
}
|
||||
|
||||
addListenerToEmailField() {
|
||||
$('#user_email').on('input', (event) => {
|
||||
this.setExternalCheckbox(event.currentTarget.value);
|
||||
});
|
||||
}
|
||||
|
||||
addListenerToUserExternalCheckbox() {
|
||||
this.external.on('click', () => {
|
||||
this.warningMessage.addClass('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
isEmailInternal(email) {
|
||||
const regex = new RegExp(this.regexPattern, this.regexOptions);
|
||||
return regex.test(email);
|
||||
}
|
||||
|
||||
setExternalCheckbox(email) {
|
||||
const isChecked = this.external.prop('checked');
|
||||
if (this.isEmailInternal(email)) {
|
||||
if (isChecked) {
|
||||
this.external.prop('checked', false);
|
||||
this.warningMessage.removeClass('hidden');
|
||||
}
|
||||
} else if (!isChecked) {
|
||||
this.external.prop('checked', true);
|
||||
this.warningMessage.addClass('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// eslint-disable-next-line
|
||||
new UserInternalRegexHandler();
|
||||
});
|
|
@ -3,6 +3,6 @@ gl-emoji {
|
|||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1.5em;
|
||||
line-height: 0.9;
|
||||
font-size: 1.4em;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
|
|
@ -111,3 +111,42 @@ body {
|
|||
.with-performance-bar .layout-page {
|
||||
margin-top: $header-height + $performance-bar-height;
|
||||
}
|
||||
|
||||
.fullscreen-layout {
|
||||
padding-top: 0;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
overflow: hidden;
|
||||
|
||||
> #js-peek,
|
||||
> .navbar-gitlab {
|
||||
position: static;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
.flash-container {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.alert-wrapper .flash-container .flash-alert:last-child,
|
||||
.alert-wrapper .flash-container .flash-notice:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
margin-top: 0;
|
||||
padding-bottom: 0;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
&.flash-shown {
|
||||
.content-wrapper {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -327,7 +327,7 @@ h6 {
|
|||
pre {
|
||||
font-family: $monospace-font;
|
||||
display: block;
|
||||
padding: $gl-padding-8;
|
||||
padding: $gl-padding-8 $input-horizontal-padding;
|
||||
margin: 0 0 $gl-padding-8;
|
||||
font-size: 13px;
|
||||
word-break: break-all;
|
||||
|
|
|
@ -236,6 +236,7 @@ $gl-vert-padding: 6px;
|
|||
$gl-padding-top: 10px;
|
||||
$gl-sidebar-padding: 22px;
|
||||
$gl-bar-padding: 3px;
|
||||
$input-horizontal-padding: 12px;
|
||||
|
||||
/*
|
||||
* Misc
|
||||
|
|
|
@ -28,11 +28,10 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
|
|||
|
||||
.ide-view {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: calc(100vh - #{$header-height});
|
||||
margin-top: 0;
|
||||
padding-bottom: $ide-statusbar-height;
|
||||
color: $gl-text-color;
|
||||
min-height: 0; // firefox fix
|
||||
|
||||
&.is-collapsed {
|
||||
.ide-file-list {
|
||||
|
@ -50,7 +49,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
min-height: 0; // firefox fix
|
||||
|
||||
.file {
|
||||
height: 32px;
|
||||
|
@ -357,7 +356,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
|
|||
|
||||
.multi-file-editor-holder {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
min-height: 0; // firefox fix
|
||||
|
||||
&.is-readonly,
|
||||
.editor.original {
|
||||
|
@ -546,7 +545,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
|
|||
border-left: 1px solid $white-dark;
|
||||
border-top: 1px solid $white-dark;
|
||||
border-top-left-radius: $border-radius-small;
|
||||
min-height: 0;
|
||||
min-height: 0; // firefox fix
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -758,7 +757,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
|
|||
|
||||
.ide-loading {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
@ -772,60 +771,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
|
|||
|
||||
.ide {
|
||||
overflow: hidden;
|
||||
|
||||
&.nav-only {
|
||||
padding-top: $header-height;
|
||||
|
||||
.with-performance-bar & {
|
||||
padding-top: $header-height + $performance-bar-height;
|
||||
}
|
||||
|
||||
.flash-container {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.alert-wrapper .flash-container .flash-alert:last-child,
|
||||
.alert-wrapper .flash-container .flash-notice:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
margin-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&.flash-shown {
|
||||
.content-wrapper {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ide-view {
|
||||
height: calc(100vh - #{$header-height + $flash-height});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.with-performance-bar .ide.nav-only {
|
||||
.flash-container {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
margin-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.ide-view {
|
||||
height: calc(100vh - #{$header-height + $performance-bar-height});
|
||||
}
|
||||
|
||||
&.flash-shown {
|
||||
.ide-view {
|
||||
height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height});
|
||||
}
|
||||
}
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
|
@ -1199,7 +1145,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
|
|||
}
|
||||
|
||||
.avatar-container {
|
||||
flex: initial;
|
||||
flex: 0 0 auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
|
@ -1209,7 +1155,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
|
|||
}
|
||||
|
||||
.ide-context-body {
|
||||
min-height: 0;
|
||||
min-height: 0; // firefox fix
|
||||
}
|
||||
|
||||
.ide-sidebar-project-title {
|
||||
|
|
|
@ -25,10 +25,6 @@
|
|||
color: $gl-text-color;
|
||||
border-radius: 0 0 3px 3px;
|
||||
|
||||
.code {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.unfold {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -141,6 +141,9 @@ ul.notes {
|
|||
}
|
||||
|
||||
.note-body {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
.note-text {
|
||||
@include md-typography;
|
||||
// Reset ul style types since we're nested inside a ul already
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module SendsBlob
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include BlobHelper
|
||||
include SendFileUpload
|
||||
end
|
||||
|
||||
def send_blob(blob, params = {})
|
||||
if blob
|
||||
headers['X-Content-Type-Options'] = 'nosniff'
|
||||
|
||||
return if cached_blob?(blob)
|
||||
|
||||
if blob.stored_externally?
|
||||
send_lfs_object(blob)
|
||||
else
|
||||
send_git_blob(repository, blob, params)
|
||||
end
|
||||
else
|
||||
render_404
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cached_blob?(blob)
|
||||
stale = stale?(etag: blob.id) # The #stale? method sets cache headers.
|
||||
|
||||
# Because we are opinionated we set the cache headers ourselves.
|
||||
response.cache_control[:public] = project.public?
|
||||
|
||||
response.cache_control[:max_age] =
|
||||
if @ref && @commit && @ref == @commit.id # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
# This is a link to a commit by its commit SHA. That means that the blob
|
||||
# is immutable. The only reason to invalidate the cache is if the commit
|
||||
# was deleted or if the user lost access to the repository.
|
||||
Blob::CACHE_TIME_IMMUTABLE
|
||||
else
|
||||
# A branch or tag points at this blob. That means that the expected blob
|
||||
# value may change over time.
|
||||
Blob::CACHE_TIME
|
||||
end
|
||||
|
||||
response.etag = blob.id
|
||||
!stale
|
||||
end
|
||||
|
||||
def send_lfs_object(blob)
|
||||
lfs_object = find_lfs_object(blob)
|
||||
|
||||
if lfs_object && lfs_object.project_allowed_access?(project)
|
||||
send_upload(lfs_object.file, attachment: blob.name)
|
||||
else
|
||||
render_404
|
||||
end
|
||||
end
|
||||
|
||||
def find_lfs_object(blob)
|
||||
lfs_object = LfsObject.find_by_oid(blob.lfs_oid)
|
||||
if lfs_object && lfs_object.file.exists?
|
||||
lfs_object
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
class IdeController < ApplicationController
|
||||
layout 'nav_only'
|
||||
layout 'fullscreen'
|
||||
|
||||
def index
|
||||
end
|
||||
|
|
|
@ -1,24 +1,16 @@
|
|||
class Projects::AvatarsController < Projects::ApplicationController
|
||||
include BlobHelper
|
||||
include SendsBlob
|
||||
|
||||
before_action :authorize_admin_project!, only: [:destroy]
|
||||
|
||||
def show
|
||||
@blob = @repository.blob_at_branch(@repository.root_ref, @project.avatar_in_git)
|
||||
if @blob
|
||||
headers['X-Content-Type-Options'] = 'nosniff'
|
||||
|
||||
return if cached_blob?
|
||||
|
||||
send_git_blob @repository, @blob
|
||||
else
|
||||
render_404
|
||||
end
|
||||
send_blob(@blob)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@project.remove_avatar!
|
||||
|
||||
@project.save
|
||||
|
||||
redirect_to edit_project_path(@project, anchor: 'js-general-project-settings'), status: :found
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# Controller for viewing a file's raw
|
||||
class Projects::RawController < Projects::ApplicationController
|
||||
include ExtractsPath
|
||||
include BlobHelper
|
||||
include SendFileUpload
|
||||
include SendsBlob
|
||||
|
||||
before_action :require_non_empty_project
|
||||
before_action :assign_ref_vars
|
||||
|
@ -10,39 +9,7 @@ class Projects::RawController < Projects::ApplicationController
|
|||
|
||||
def show
|
||||
@blob = @repository.blob_at(@commit.id, @path)
|
||||
if @blob
|
||||
headers['X-Content-Type-Options'] = 'nosniff'
|
||||
|
||||
return if cached_blob?
|
||||
|
||||
if @blob.stored_externally?
|
||||
send_lfs_object
|
||||
else
|
||||
send_git_blob @repository, @blob, inline: (params[:inline] != 'false')
|
||||
end
|
||||
else
|
||||
render_404
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_lfs_object
|
||||
lfs_object = find_lfs_object
|
||||
|
||||
if lfs_object && lfs_object.project_allowed_access?(@project)
|
||||
send_upload(lfs_object.file, attachment: @blob.name)
|
||||
else
|
||||
render_404
|
||||
end
|
||||
end
|
||||
|
||||
def find_lfs_object
|
||||
lfs_object = LfsObject.find_by_oid(@blob.lfs_oid)
|
||||
if lfs_object && lfs_object.file.exists?
|
||||
lfs_object
|
||||
else
|
||||
nil
|
||||
end
|
||||
send_blob(@blob, inline: (params[:inline] != 'false'))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -255,6 +255,7 @@ module ApplicationSettingsHelper
|
|||
:instance_statistics_visibility_private,
|
||||
:user_default_external,
|
||||
:user_show_add_ssh_key_message,
|
||||
:user_default_internal_regex,
|
||||
:user_oauth_applications,
|
||||
:version_check_enabled,
|
||||
:web_ide_clientside_preview_enabled
|
||||
|
|
|
@ -157,28 +157,6 @@ module BlobHelper
|
|||
end
|
||||
end
|
||||
|
||||
def cached_blob?
|
||||
stale = stale?(etag: @blob.id) # The #stale? method sets cache headers.
|
||||
|
||||
# Because we are opionated we set the cache headers ourselves.
|
||||
response.cache_control[:public] = @project.public?
|
||||
|
||||
response.cache_control[:max_age] =
|
||||
if @ref && @commit && @ref == @commit.id
|
||||
# This is a link to a commit by its commit SHA. That means that the blob
|
||||
# is immutable. The only reason to invalidate the cache is if the commit
|
||||
# was deleted or if the user lost access to the repository.
|
||||
Blob::CACHE_TIME_IMMUTABLE
|
||||
else
|
||||
# A branch or tag points at this blob. That means that the expected blob
|
||||
# value may change over time.
|
||||
Blob::CACHE_TIME
|
||||
end
|
||||
|
||||
response.etag = @blob.id
|
||||
!stale
|
||||
end
|
||||
|
||||
def licenses_for_select
|
||||
return @licenses_for_select if defined?(@licenses_for_select)
|
||||
|
||||
|
|
|
@ -64,8 +64,7 @@ module SubmoduleHelper
|
|||
end
|
||||
|
||||
def relative_self_url?(url)
|
||||
# (./)?(../repo.git) || (./)?(../../project/repo.git) )
|
||||
url =~ %r{\A((\./)?(\.\./))(?!(\.\.)|(.*/)).*(\.git)?\z} || url =~ %r{\A((\./)?(\.\./){2})(?!(\.\.))([^/]*)/(?!(\.\.)|(.*/)).*(\.git)?\z}
|
||||
url.start_with?('../', './')
|
||||
end
|
||||
|
||||
def standard_links(host, namespace, project, commit)
|
||||
|
@ -73,25 +72,29 @@ module SubmoduleHelper
|
|||
[base, [base, '/tree/', commit].join('')]
|
||||
end
|
||||
|
||||
def relative_self_links(url, commit, project)
|
||||
url.rstrip!
|
||||
# Map relative links to a namespace and project
|
||||
# For example:
|
||||
# ../bar.git -> same namespace, repo bar
|
||||
# ../foo/bar.git -> namespace foo, repo bar
|
||||
# ../../foo/bar/baz.git -> namespace bar, repo baz
|
||||
components = url.split('/')
|
||||
base = components.pop.gsub(/.git$/, '')
|
||||
namespace = components.pop.gsub(/^\.\.$/, '')
|
||||
def relative_self_links(relative_path, commit, project)
|
||||
relative_path.rstrip!
|
||||
absolute_project_path = "/" + project.full_path
|
||||
|
||||
if namespace.empty?
|
||||
namespace = project.namespace.full_path
|
||||
# Resolve `relative_path` to target path
|
||||
# Assuming `absolute_project_path` is `/g1/p1`:
|
||||
# ../p2.git -> /g1/p2
|
||||
# ../g2/p3.git -> /g1/g2/p3
|
||||
# ../../g3/g4/p4.git -> /g3/g4/p4
|
||||
submodule_project_path = File.absolute_path(relative_path, absolute_project_path)
|
||||
target_namespace_path = File.dirname(submodule_project_path)
|
||||
|
||||
if target_namespace_path == '/' || target_namespace_path.start_with?(absolute_project_path)
|
||||
return [nil, nil]
|
||||
end
|
||||
|
||||
target_namespace_path.sub!(%r{^/}, '')
|
||||
submodule_base = File.basename(submodule_project_path, '.git')
|
||||
|
||||
begin
|
||||
[
|
||||
namespace_project_path(namespace, base),
|
||||
namespace_project_tree_path(namespace, base, commit)
|
||||
namespace_project_path(target_namespace_path, submodule_base),
|
||||
namespace_project_tree_path(target_namespace_path, submodule_base, commit)
|
||||
]
|
||||
rescue ActionController::UrlGenerationError
|
||||
[nil, nil]
|
||||
|
|
|
@ -23,6 +23,17 @@ module UsersHelper
|
|||
profile_tabs.include?(tab)
|
||||
end
|
||||
|
||||
def user_internal_regex_data
|
||||
settings = Gitlab::CurrentSettings.current_application_settings
|
||||
|
||||
pattern, options = if settings.user_default_internal_regex_enabled?
|
||||
regex = settings.user_default_internal_regex_instance
|
||||
JsRegex.new(regex).to_h.slice(:source, :options).values
|
||||
end
|
||||
|
||||
{ user_internal_regex_pattern: pattern, user_internal_regex_options: options }
|
||||
end
|
||||
|
||||
def current_user_menu_items
|
||||
@current_user_menu_items ||= get_current_user_menu_items
|
||||
end
|
||||
|
|
|
@ -192,6 +192,8 @@ class ApplicationSetting < ActiveRecord::Base
|
|||
numericality: { less_than_or_equal_to: :gitaly_timeout_default },
|
||||
if: :gitaly_timeout_default
|
||||
|
||||
validates :user_default_internal_regex, js_regex: true, allow_nil: true
|
||||
|
||||
SUPPORTED_KEY_TYPES.each do |type|
|
||||
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
|
||||
end
|
||||
|
@ -299,6 +301,7 @@ class ApplicationSetting < ActiveRecord::Base
|
|||
usage_ping_enabled: Settings.gitlab['usage_ping_enabled'],
|
||||
instance_statistics_visibility_private: false,
|
||||
user_default_external: false,
|
||||
user_default_internal_regex: nil,
|
||||
user_show_add_ssh_key_message: true
|
||||
}
|
||||
end
|
||||
|
@ -435,6 +438,14 @@ class ApplicationSetting < ActiveRecord::Base
|
|||
password_authentication_enabled_for_web? || password_authentication_enabled_for_git?
|
||||
end
|
||||
|
||||
def user_default_internal_regex_enabled?
|
||||
user_default_external? && user_default_internal_regex.present?
|
||||
end
|
||||
|
||||
def user_default_internal_regex_instance
|
||||
Regexp.new(user_default_internal_regex, Regexp::IGNORECASE)
|
||||
end
|
||||
|
||||
delegate :terms, to: :latest_terms, allow_nil: true
|
||||
def latest_terms
|
||||
@latest_terms ||= Term.latest
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
module AtomicInternalId
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def has_internal_id(column, scope:, init:, presence: true) # rubocop:disable Naming/PredicateName
|
||||
# We require init here to retain the ability to recalculate in the absence of a
|
||||
# InternaLId record (we may delete records in `internal_ids` for example).
|
||||
|
|
|
@ -12,7 +12,7 @@ module Awardable
|
|||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def awarded(user, name)
|
||||
sql = <<~EOL
|
||||
EXISTS (
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
module CaseSensitivity
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# Queries the given columns regardless of the casing used.
|
||||
#
|
||||
# Unlike other ActiveRecord methods this method only operates on a Hash.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module EachBatch
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# Iterates over the rows in a relation in batches, similar to Rails'
|
||||
# `in_batches` but in a more efficient way.
|
||||
#
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
module IgnorableColumn
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def columns
|
||||
super.reject { |column| ignored_columns.include?(column.name) }
|
||||
end
|
||||
|
|
|
@ -118,7 +118,7 @@ module Issuable
|
|||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# Searches for records with a matching title.
|
||||
#
|
||||
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module LoadedInGroupList
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def with_counts(archived:)
|
||||
selects_including_counts = [
|
||||
'namespaces.*',
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module ManualInverseAssociation
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def manual_inverse_association(association, inverse)
|
||||
define_method(association) do |*args|
|
||||
super(*args).tap do |value|
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
module Mentionable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# Indicate which attributes of the Mentionable to search for GFM references.
|
||||
def attr_mentionable(attr, options = {})
|
||||
attr = attr.to_s
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module OptionallySearch
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def search(*)
|
||||
raise(
|
||||
NotImplementedError,
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
module Participable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# Adds a list of participant attributes. Attributes can either be symbols or
|
||||
# Procs.
|
||||
#
|
||||
|
|
|
@ -40,7 +40,7 @@ module Referable
|
|||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# The character that prefixes the actual reference identifier
|
||||
#
|
||||
# This should be overridden by the including class.
|
||||
|
|
|
@ -20,7 +20,7 @@ module ResolvableNote
|
|||
scope :unresolved, -> { resolvable.where(resolved_at: nil) }
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# This method must be kept in sync with `#resolve!`
|
||||
def resolve!(current_user)
|
||||
unresolved.update_all(resolved_at: Time.now, resolved_by_id: current_user.id)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module SelectForProjectAuthorization
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def select_for_project_authorization
|
||||
select("projects.id AS project_id, members.access_level")
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module ShaAttribute
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def sha_attribute(name)
|
||||
return if ENV['STATIC_VERIFICATION']
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ module Sortable
|
|||
scope :order_name_desc, -> { reorder(Arel::Nodes::Descending.new(arel_table[:name].lower)) }
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def order_by(method)
|
||||
case method.to_s
|
||||
when 'created_asc' then order_created_asc
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Spammable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def attr_spammable(attr, options = {})
|
||||
spammable_attrs << [attr.to_s, options]
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
module StripAttribute
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def strip_attributes(*attrs)
|
||||
strip_attrs.concat(attrs)
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ module TriggerableHooks
|
|||
push_hooks: :push_events,
|
||||
tag_push_hooks: :tag_push_events,
|
||||
issue_hooks: :issues_events,
|
||||
confidential_note_hooks: :confidential_note_events,
|
||||
confidential_issue_hooks: :confidential_issues_events,
|
||||
note_hooks: :note_events,
|
||||
merge_request_hooks: :merge_requests_events,
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
class DiffFileEntity < Grape::Entity
|
||||
include RequestAwareEntity
|
||||
include BlobHelper
|
||||
include CommitsHelper
|
||||
include DiffHelper
|
||||
include SubmoduleHelper
|
||||
|
|
|
@ -43,8 +43,8 @@ module Projects
|
|||
@new_path = File.join(@new_namespace.try(:full_path) || '', project.path)
|
||||
@old_namespace = project.namespace
|
||||
|
||||
if Project.where(path: project.path, namespace_id: @new_namespace.try(:id)).exists?
|
||||
raise TransferError.new("Project with same path in target namespace already exists")
|
||||
if Project.where(namespace_id: @new_namespace.try(:id)).where('path = ? or name = ?', project.path, project.name).exists?
|
||||
raise TransferError.new("Project with same name or path in target namespace already exists")
|
||||
end
|
||||
|
||||
if project.has_container_registry_tags?
|
||||
|
@ -118,6 +118,7 @@ module Projects
|
|||
|
||||
def rollback_side_effects
|
||||
rollback_folder_move
|
||||
project.reload
|
||||
update_namespace_and_visibility(@old_namespace)
|
||||
write_repository_config(@old_path)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
module Users
|
||||
class BuildService < BaseService
|
||||
delegate :user_default_internal_regex_enabled?,
|
||||
:user_default_internal_regex_instance,
|
||||
to: :'Gitlab::CurrentSettings.current_application_settings'
|
||||
|
||||
def initialize(current_user, params = {})
|
||||
@current_user = current_user
|
||||
@params = params.dup
|
||||
|
@ -89,6 +93,10 @@ module Users
|
|||
if params[:reset_password]
|
||||
user_params.merge!(force_random_password: true, password_expires_at: nil)
|
||||
end
|
||||
|
||||
if user_default_internal_regex_enabled? && !user_params.key?(:external)
|
||||
user_params[:external] = user_external?
|
||||
end
|
||||
else
|
||||
allowed_signup_params = signup_params
|
||||
allowed_signup_params << :skip_confirmation if skip_authorization
|
||||
|
@ -105,5 +113,9 @@ module Users
|
|||
def skip_user_confirmation_email_from_setting
|
||||
!Gitlab::CurrentSettings.send_user_confirmation_email
|
||||
end
|
||||
|
||||
def user_external?
|
||||
user_default_internal_regex_instance.match(params[:email]).nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
class JsRegexValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
return true if value.blank?
|
||||
|
||||
parsed_regex = JsRegex.new(Regexp.new(value, Regexp::IGNORECASE))
|
||||
|
||||
if parsed_regex.source.empty?
|
||||
record.errors.add(attribute, "Regex Pattern #{value} can not be expressed in Javascript")
|
||||
else
|
||||
parsed_regex.warnings.each { |warning| record.errors.add(attribute, warning) }
|
||||
end
|
||||
rescue RegexpError => regex_error
|
||||
record.errors.add(attribute, regex_error.to_s)
|
||||
end
|
||||
end
|
|
@ -29,6 +29,13 @@
|
|||
= f.check_box :user_default_external, class: 'form-check-input'
|
||||
= f.label :user_default_external, class: 'form-check-label' do
|
||||
Newly registered users will by default be external
|
||||
.prepend-top-10
|
||||
= _('Internal users')
|
||||
= f.text_field :user_default_internal_regex, placeholder: _('Regex pattern'), class: 'form-control prepend-top-5'
|
||||
.help-block
|
||||
= _('Specify an e-mail address regex pattern to identify default internal users.')
|
||||
= link_to _('More information'), help_page_path('user/permissions', anchor: 'external-users-permissions'),
|
||||
target: '_blank'
|
||||
.form-group
|
||||
= f.label :user_show_add_ssh_key_message, 'Prompt users to upload SSH keys', class: 'label-bold'
|
||||
.form-check
|
||||
|
|
|
@ -34,8 +34,12 @@
|
|||
.form-group.row
|
||||
.col-sm-2.text-right
|
||||
= f.label :external, class: 'col-form-label'
|
||||
.hidden{ data: user_internal_regex_data }
|
||||
.col-sm-10
|
||||
= f.check_box :external do
|
||||
External
|
||||
%p.light
|
||||
External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects or groups.
|
||||
%row.hidden#warning_external_automatically_set.hidden
|
||||
.badge.badge-warning.text-white
|
||||
= _('Automatically marked as default internal user')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
- @body_class = 'ide'
|
||||
- @body_class = 'ide-layout'
|
||||
- page_title 'IDE'
|
||||
|
||||
- content_for :page_specific_javascripts do
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
!!! 5
|
||||
%html{ lang: I18n.locale, class: page_class }
|
||||
= render "layouts/head"
|
||||
%body{ class: "#{user_application_theme} #{@body_class} nav-only", data: { page: body_data_page } }
|
||||
%body{ class: "#{user_application_theme} #{@body_class} fullscreen-layout", data: { page: body_data_page } }
|
||||
= render 'peek/bar'
|
||||
= render "layouts/header/default"
|
||||
= render 'shared/outdated_browser'
|
||||
|
@ -10,5 +10,5 @@
|
|||
= render "layouts/broadcast"
|
||||
= yield :flash_message
|
||||
= render "layouts/flash"
|
||||
.content{ id: "content-body" }
|
||||
.content-wrapper{ id: "content-body", class: "d-flex flex-column align-items-stretch" }
|
||||
= yield
|
|
@ -30,11 +30,13 @@
|
|||
%pre.dark#merge-info-3
|
||||
- if @merge_request.for_fork?
|
||||
:preserve
|
||||
git checkout #{h @merge_request.target_branch}
|
||||
git fetch origin
|
||||
git checkout origin/#{h @merge_request.target_branch}
|
||||
git merge --no-ff #{h @merge_request.source_project_path}-#{h @merge_request.source_branch}
|
||||
- else
|
||||
:preserve
|
||||
git checkout #{h @merge_request.target_branch}
|
||||
git fetch origin
|
||||
git checkout origin/#{h @merge_request.target_branch}
|
||||
git merge --no-ff #{h @merge_request.source_branch}
|
||||
%p
|
||||
%strong Step 4.
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.banner-callout.compact.milestone-deprecation-message.prepend-top-20
|
||||
.banner-graphic= image_tag 'illustrations/milestone_removing-page.svg'
|
||||
.banner-body.prepend-left-10.append-right-10
|
||||
%h5.banner-title.prepend-top-0
|
||||
= _('The tabs below will be removed in a future version')
|
||||
%p.milestone-banner-text
|
||||
= _('Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}.').html_safe % { issue_boards_url: link_to(_('issue boards'), help_page_url('user/project/issue_board'), target: '_blank', rel: 'noopener noreferrer'), gitlab_issues_url: link_to(_('GitLab’s issue tracker'), 'https://gitlab.com/gitlab-org/gitlab-ce/issues', target: '_blank', rel: 'noopener noreferrer') }
|
|
@ -67,5 +67,6 @@
|
|||
.alert.alert-success.prepend-top-default
|
||||
%span All issues for this milestone are closed. You may close this milestone now.
|
||||
|
||||
= render 'deprecation_message'
|
||||
= render 'shared/milestones/tabs', milestone: @milestone
|
||||
= render 'shared/milestones/sidebar', milestone: @milestone, project: @project, affix_offset: 153
|
||||
|
|
|
@ -10,17 +10,7 @@ class BackgroundMigrationWorker
|
|||
# maintenance related tasks have plenty of time to clean up after a migration
|
||||
# has been performed.
|
||||
def self.minimum_interval
|
||||
if enable_health_check?
|
||||
2.minutes.to_i
|
||||
else
|
||||
5.minutes.to_i
|
||||
end
|
||||
end
|
||||
|
||||
def self.enable_health_check?
|
||||
Rails.env.development? ||
|
||||
Rails.env.test? ||
|
||||
Feature.enabled?('background_migration_health_check')
|
||||
2.minutes.to_i
|
||||
end
|
||||
|
||||
# Performs the background migration.
|
||||
|
@ -86,8 +76,6 @@ class BackgroundMigrationWorker
|
|||
# class_name - The name of the background migration that we might want to
|
||||
# run.
|
||||
def healthy_database?
|
||||
return true unless self.class.enable_health_check?
|
||||
|
||||
return true unless Gitlab::Database.postgresql?
|
||||
|
||||
!Postgresql::ReplicationSlot.lag_too_great?
|
||||
|
|
|
@ -11,7 +11,7 @@ module ApplicationWorker
|
|||
set_queue
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def inherited(subclass)
|
||||
subclass.set_queue
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module WaitableWorker
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# Schedules multiple jobs and waits for them to be completed.
|
||||
def bulk_perform_and_wait(args_list, timeout: 10)
|
||||
# Short-circuit: it's more efficient to do small numbers of jobs inline
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix git submodule link for subgroup projects with relative path
|
||||
merge_request: 21154
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix project transfer name validation issues causing a redirect loop
|
||||
merge_request: 21408
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix IDE issues with persistent banners
|
||||
merge_request: 21283
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Importing a project no longer fails when visibility level holds a string value
|
||||
type
|
||||
merge_request: 21242
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Show deprecation message on project milestone page for category tabs
|
||||
merge_request: 21236
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Adds Rubocop rule to enforce class_methods over module ClassMethods
|
||||
merge_request: 21379
|
||||
author: Jacopo Beschi @jacopo-beschi
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add default parameter to branches API
|
||||
merge_request: 21294
|
||||
author: Riccardo Padovani
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add an option to whitelist users based on email address as internal when the "New user set to external" setting is enabled.
|
||||
merge_request: 17711
|
||||
author: Roger Rüttimann
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Emojis cutting in the right way
|
||||
merge_request:
|
||||
author: Alexander Popov
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed bug when the project logo file is stored in LFS
|
||||
merge_request: 20948
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enabled multiple file uploads in the Web IDE
|
||||
merge_request:
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed IDE file row scrolling into view when hovering
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove health check feature flag in BackgroundMigrationWorker
|
||||
merge_request:
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Backport schema_changed.sh from EE which prints the diff if the schema is different
|
||||
merge_request: 21422
|
||||
author: Jasper Maes
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix "Confidential comments" button not saving in project hooks
|
||||
merge_request: 21289
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Error 500s due to encoding issues when Wiki hooks fire
|
||||
merge_request: 21414
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Increase padding in code blocks
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,13 @@
|
|||
class AddUserInternalRegexToApplicationSetting < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
add_column :application_settings, :user_default_internal_regex, :string, null: true
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :application_settings, :user_default_internal_regex
|
||||
end
|
||||
end
|
|
@ -164,6 +164,7 @@ ActiveRecord::Schema.define(version: 20180826111825) do
|
|||
t.boolean "authorized_keys_enabled", default: true, null: false
|
||||
t.string "auto_devops_domain"
|
||||
t.boolean "pages_domain_verification_enabled", default: true, null: false
|
||||
t.string "user_default_internal_regex"
|
||||
t.boolean "allow_local_requests_from_hooks_and_services", default: false, null: false
|
||||
t.boolean "enforce_terms", default: false
|
||||
t.boolean "mirror_available", default: true, null: false
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Compliance features
|
||||
|
||||
You can configure the following GitLab features to help ensure that your GitLab instance meets common compliance standards. Click a feature name for further documentation.
|
||||
|
||||
GitLab’s [security features](../security/README.md) may also help you meet relevant compliance standards.
|
||||
|
||||
|Feature |GitLab tier |GitLab.com |
|
||||
| ---------| :--------: | :-------: |
|
||||
|**[Restrict SSH Keys](../README.html#administrator-documentation)**<br>Control the technology and key length of SSH keys used to access GitLab|Core+||
|
||||
|**[Granular user roles and flexible permissions](../user/permissions.html)**<br>Manage access and permissions with five different user roles and settings for external users. Set permissions according to people's role, rather than either read or write access to a repository. Don't share the source code with people that only need access to the issue tracker.|Core+|✓|
|
||||
|**[Enforce TOS acceptance](../user/admin_area/settings/terms.html)**<br>Enforce your users accepting new terms of service by blocking GitLab traffic.|Core+||
|
||||
|**[Email all users of a project, group, or entire server](../user/admin_area/settings/terms.html)**<br>An admin can email groups of users based on project or group membership, or email everyone using the GitLab instance. This is great for scheduled maintenance or upgrades.|Starter+||
|
||||
|**[Omnibus package supports log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-forwarding)**<br>Forward your logs to a central system.|Starter+||
|
||||
|**[Lock project membership to group](../workflow/groups.html#lock-project-membership-to-members-of-this-group)**<br>Group owners can prevent new members from being added to projects within a group.|Starter+|✓|
|
||||
|**[LDAP group sync](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#group-sync)**<br>GitLab Enterprise Edition gives admins the ability to automatically sync groups and manage SSH keys, permissions, and authentication, so you can focus on building your product, not configuring your tools.|Starter+||
|
||||
|**[LDAP group sync filters](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#group-sync)**<br>GitLab Enterprise Edition Premium gives more flexibility to synchronize with LDAP based on filters, meaning you can leverage LDAP attributes to map GitLab permissions.|Premium+||
|
||||
|**[Audit logs](https://docs.gitlab.com/ee/administration/audit_events.html)**<br>To maintain the integrity of your code, GitLab Enterprise Edition Premium gives admins the ability to view any modifications made within the GitLab server in an advanced audit log system, so you can control, analyze and track every change.|Premium+||
|
||||
|**[Auditor users](https://docs.gitlab.com/ee/administration/auditor_users.html)**<br>Auditor users are users who are given read-only access to all projects, groups, and other resources on the GitLab instance.|Premium+||
|
|
@ -46,6 +46,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
|
|||
- [Plugins](plugins.md): With custom plugins, GitLab administrators can introduce custom integrations without modifying GitLab's source code.
|
||||
- [Enforcing Terms of Service](../user/admin_area/settings/terms.md)
|
||||
- [Third party offers](../user/admin_area/settings/third_party_offers.md)
|
||||
- [Compliance](compliance.md): A collection of features from across the application that you may configure to help ensure that your GitLab instance and DevOps workflow meet compliance standards.
|
||||
|
||||
#### Customizing GitLab's appearance
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ Example response:
|
|||
"name": "master",
|
||||
"merged": false,
|
||||
"protected": true,
|
||||
"default": true,
|
||||
"developers_can_push": false,
|
||||
"developers_can_merge": false,
|
||||
"can_push": true,
|
||||
|
@ -75,6 +76,7 @@ Example response:
|
|||
"name": "master",
|
||||
"merged": false,
|
||||
"protected": true,
|
||||
"default": true,
|
||||
"developers_can_push": false,
|
||||
"developers_can_merge": false,
|
||||
"can_push": true,
|
||||
|
@ -141,6 +143,7 @@ Example response:
|
|||
"name": "master",
|
||||
"merged": false,
|
||||
"protected": true,
|
||||
"default": true,
|
||||
"developers_can_push": true,
|
||||
"developers_can_merge": true,
|
||||
"can_push": true
|
||||
|
@ -190,6 +193,7 @@ Example response:
|
|||
"name": "master",
|
||||
"merged": false,
|
||||
"protected": false,
|
||||
"default": true,
|
||||
"developers_can_push": false,
|
||||
"developers_can_merge": false,
|
||||
"can_push": true
|
||||
|
@ -234,6 +238,7 @@ Example response:
|
|||
"name": "newbranch",
|
||||
"merged": false,
|
||||
"protected": false,
|
||||
"default": false,
|
||||
"developers_can_push": false,
|
||||
"developers_can_merge": false,
|
||||
"can_push": true
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 60 KiB |
|
@ -197,7 +197,7 @@ They will, like usual users, receive a role in the project or group with all
|
|||
the abilities that are mentioned in the table above. They cannot however create
|
||||
groups or projects, and they have the same access as logged out users in all
|
||||
other cases.
|
||||
|
||||
|
||||
An administrator can flag a user as external [through the API](../api/users.md)
|
||||
or by checking the checkbox on the admin panel. As an administrator, navigate
|
||||
to **Admin > Users** to create a new user or edit an existing one. There, you
|
||||
|
@ -206,6 +206,21 @@ will find the option to flag the user as external.
|
|||
By default new users are not set as external users. This behavior can be changed
|
||||
by an administrator under **Admin > Application Settings**.
|
||||
|
||||
### Default internal users
|
||||
|
||||
The "Internal users" field allows specifying an e-mail address regex pattern to identify default internal users.
|
||||
|
||||
New users whose email address matches the regex pattern will be set to internal by default rather than an external collaborator.
|
||||
|
||||
The regex pattern format is Ruby, but it needs to be convertible to JavaScript, and the ignore case flag will be set, e.g. "/regex pattern/i".
|
||||
|
||||
Here are some examples:
|
||||
|
||||
- Use `\.internal@domain\.com` to mark email addresses containing ".internal@domain.com" internal.
|
||||
- Use `^(?:(?!\.ext@domain\.com).)*$\r?` to mark users with email addresses NOT including .ext@domain.com internal.
|
||||
|
||||
Please be aware that this regex could lead to a DOS attack, [see](https://en.wikipedia.org/wiki/ReDoS?) ReDos on Wikipedia.
|
||||
|
||||
## Auditor users **[PREMIUM ONLY]**
|
||||
|
||||
>[Introduced][ee-998] in [GitLab Premium][eep] 8.17.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## On Microsoft Teams
|
||||
|
||||
To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft Teams by following the steps described in this [document](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors#setting-up-a-custom-incoming-webhook).
|
||||
To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft Teams by following the steps described in this [document](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors/connectors-using#setting-up-a-custom-incoming-webhook).
|
||||
|
||||
## On GitLab
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ module API
|
|||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
private
|
||||
|
||||
def install_error_responders(base)
|
||||
|
|
|
@ -370,6 +370,10 @@ module API
|
|||
expose :can_push do |repo_branch, options|
|
||||
Gitlab::UserAccess.new(options[:current_user], project: options[:project]).can_push_to_branch?(repo_branch.name)
|
||||
end
|
||||
|
||||
expose :default do |repo_branch, options|
|
||||
options[:project].default_branch == repo_branch.name
|
||||
end
|
||||
end
|
||||
|
||||
class TreeObject < Grape::Entity
|
||||
|
|
|
@ -2,7 +2,7 @@ module API
|
|||
module ProjectsRelationBuilder
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def prepare_relation(projects_relation, options = {})
|
||||
projects_relation = preload_relation(projects_relation, options)
|
||||
execute_batch_counting(projects_relation)
|
||||
|
|
|
@ -75,7 +75,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def binary_stringio(str)
|
||||
StringIO.new(str || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
|
||||
StringIO.new(str.freeze || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -233,6 +233,8 @@ module Gitlab
|
|||
end
|
||||
elsif user
|
||||
# User access is verified in check_change_access!
|
||||
elsif authed_via_jwt?
|
||||
# Authenticated via JWT
|
||||
else
|
||||
raise UnauthorizedError, ERROR_MESSAGES[:upload]
|
||||
end
|
||||
|
@ -321,6 +323,10 @@ module Gitlab
|
|||
!Gitlab.config.gitlab_shell.receive_pack
|
||||
end
|
||||
|
||||
def authed_via_jwt?
|
||||
false
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def changes_list
|
||||
|
|
|
@ -6,7 +6,7 @@ module Gitlab
|
|||
module ExposeAttribute
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# Defines getter methods for the given attribute names.
|
||||
#
|
||||
# Example:
|
||||
|
|
|
@ -5,7 +5,7 @@ module Gitlab
|
|||
module MountMutation
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
def mount_mutation(mutation_class)
|
||||
# Using an underscored field name symbol will make `graphql-ruby`
|
||||
# standardize the field name
|
||||
|
|
|
@ -94,7 +94,10 @@ module Gitlab
|
|||
end
|
||||
|
||||
def restore_project
|
||||
@project.update_columns(project_params)
|
||||
Gitlab::Timeless.timeless(@project) do
|
||||
@project.update(project_params)
|
||||
end
|
||||
|
||||
@project
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
module StaticModel
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
class_methods do
|
||||
# Used by ActiveRecord's polymorphic association to set object_id
|
||||
def primary_key
|
||||
'id'
|
||||
|
|
|
@ -727,6 +727,9 @@ msgstr ""
|
|||
msgid "AutoDevOps|enable Auto DevOps"
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically marked as default internal user"
|
||||
msgstr ""
|
||||
|
||||
msgid "Available"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2801,6 +2804,9 @@ msgstr ""
|
|||
msgid "GitLab.com import"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab’s issue tracker"
|
||||
msgstr ""
|
||||
|
||||
msgid "Gitaly"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3187,6 +3193,9 @@ msgstr ""
|
|||
msgid "Internal - The project can be accessed by any logged in user."
|
||||
msgstr ""
|
||||
|
||||
msgid "Internal users"
|
||||
msgstr ""
|
||||
|
||||
msgid "Interval Pattern"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3387,6 +3396,9 @@ msgstr ""
|
|||
msgid "Learn more"
|
||||
msgstr ""
|
||||
|
||||
msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple lists, using labels, assignees, and milestones. If you’re missing something from issue boards, please create an issue on %{gitlab_issues_url}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Learn more about Kubernetes"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4670,6 +4682,9 @@ msgid_plural "Refreshing in %d seconds to show the updated status..."
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "Regex pattern"
|
||||
msgstr ""
|
||||
|
||||
msgid "Register / Sign In"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5277,6 +5292,9 @@ msgstr ""
|
|||
msgid "Specific Runners"
|
||||
msgstr ""
|
||||
|
||||
msgid "Specify an e-mail address regex pattern to identify default internal users."
|
||||
msgstr ""
|
||||
|
||||
msgid "Specify the following URL during the Runner setup:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5555,6 +5573,9 @@ msgstr ""
|
|||
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
|
||||
msgstr ""
|
||||
|
||||
msgid "The tabs below will be removed in a future version"
|
||||
msgstr ""
|
||||
|
||||
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
|
||||
msgstr ""
|
||||
|
||||
|
@ -5934,6 +5955,9 @@ msgstr ""
|
|||
msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "To define internal users, first enable new users set to external"
|
||||
msgstr ""
|
||||
|
||||
msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
|
||||
msgstr ""
|
||||
|
||||
|
@ -6590,6 +6614,9 @@ msgstr ""
|
|||
msgid "importing"
|
||||
msgstr ""
|
||||
|
||||
msgid "issue boards"
|
||||
msgstr ""
|
||||
|
||||
msgid "latest version"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'pathname'
|
||||
|
||||
module QA
|
||||
module Page
|
||||
class View
|
||||
|
@ -9,7 +11,7 @@ module QA
|
|||
end
|
||||
|
||||
def pathname
|
||||
@pathname ||= Pathname.new(::File.join(__dir__, '../../../', @path))
|
||||
@pathname ||= ::Pathname.new(::File.join(__dir__, '../../../', @path))
|
||||
.cleanpath.expand_path
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
# Enforces the use of 'class_methods' instead of 'module ClassMethods' for activesupport concerns.
|
||||
# For more information see: https://gitlab.com/gitlab-org/gitlab-ce/issues/50414
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# module Foo
|
||||
# extend ActiveSupport::Concern
|
||||
#
|
||||
# module ClassMethods
|
||||
# def a_class_method
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# module Foo
|
||||
# extend ActiveSupport::Concern
|
||||
#
|
||||
# class_methods do
|
||||
# def a_class_method
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
class PreferClassMethodsOverModule < RuboCop::Cop::Cop
|
||||
include RangeHelp
|
||||
|
||||
MSG = 'Do not use module ClassMethods, use class_methods block instead.'
|
||||
|
||||
def_node_matcher :extend_activesupport_concern?, <<~PATTERN
|
||||
(:send nil? :extend (:const (:const nil? :ActiveSupport) :Concern))
|
||||
PATTERN
|
||||
|
||||
def on_module(node)
|
||||
add_offense(node) if node.defined_module_name == 'ClassMethods' && module_extends_activesupport_concern?(node)
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
lambda do |corrector|
|
||||
corrector.replace(module_range(node), 'class_methods do')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def module_extends_activesupport_concern?(node)
|
||||
container_module = container_module_of(node)
|
||||
return false unless container_module
|
||||
|
||||
container_module.descendants.any? do |descendant|
|
||||
extend_activesupport_concern?(descendant)
|
||||
end
|
||||
end
|
||||
|
||||
def container_module_of(node)
|
||||
while node = node.parent
|
||||
break if node.type == :module
|
||||
end
|
||||
|
||||
node
|
||||
end
|
||||
|
||||
def module_range(node)
|
||||
module_node, _ = *node
|
||||
range_between(node.loc.keyword.begin_pos, module_node.source_range.end_pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,6 +7,7 @@ require_relative 'cop/include_sidekiq_worker'
|
|||
require_relative 'cop/avoid_return_from_blocks'
|
||||
require_relative 'cop/avoid_break_from_strong_memoize'
|
||||
require_relative 'cop/line_break_around_conditional_block'
|
||||
require_relative 'cop/prefer_class_methods_over_module'
|
||||
require_relative 'cop/migration/add_column'
|
||||
require_relative 'cop/migration/add_concurrent_foreign_key'
|
||||
require_relative 'cop/migration/add_concurrent_index'
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
function schema_changed() {
|
||||
if [[ ! -z `git diff --name-only -- db/schema.rb` ]]; then
|
||||
echo "db/schema.rb after rake db:migrate:reset is different from one in the repository"
|
||||
#!/bin/sh
|
||||
|
||||
schema_changed() {
|
||||
if [ ! -z "$(git diff --name-only -- db/schema.rb)" ]; then
|
||||
printf "db/schema.rb after rake db:migrate:reset is different from one in the repository"
|
||||
printf "The diff is as follows:\n"
|
||||
diff=$(git diff -p --binary -- db/schema.rb)
|
||||
printf "%s" "$diff"
|
||||
exit 1
|
||||
else
|
||||
echo "db/schema.rb after rake db:migrate:reset matches one in the repository"
|
||||
printf "db/schema.rb after rake db:migrate:reset matches one in the repository"
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +1,55 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Projects::AvatarsController do
|
||||
let(:project) { create(:project, :repository, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
project.add_maintainer(user)
|
||||
controller.instance_variable_set(:@project, project)
|
||||
end
|
||||
|
||||
it 'GET #show' do
|
||||
get :show, namespace_id: project.namespace.id, project_id: project.id
|
||||
describe 'GET #show' do
|
||||
subject { get :show, namespace_id: project.namespace, project_id: project }
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
context 'when repository has no avatar' do
|
||||
it 'shows 404' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when repository has an avatar' do
|
||||
before do
|
||||
allow(project).to receive(:avatar_in_git).and_return(filepath)
|
||||
end
|
||||
|
||||
context 'when the avatar is stored in the repository' do
|
||||
let(:filepath) { 'files/images/logo-white.png' }
|
||||
|
||||
it 'sends the avatar' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(200)
|
||||
expect(response.header['Content-Type']).to eq('image/png')
|
||||
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the avatar is stored in lfs' do
|
||||
it_behaves_like 'repository lfs file load' do
|
||||
let(:filename) { 'lfs_object.iso' }
|
||||
let(:filepath) { "files/lfs/#{filename}" }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'removes avatar from DB by calling destroy' do
|
||||
delete :destroy, namespace_id: project.namespace.id, project_id: project.id
|
||||
expect(project.avatar.present?).to be_falsey
|
||||
expect(project).to be_valid
|
||||
describe 'DELETE #destroy' do
|
||||
it 'removes avatar from DB by calling destroy' do
|
||||
delete :destroy, namespace_id: project.namespace.id, project_id: project.id
|
||||
|
||||
expect(project.avatar.present?).to be_falsey
|
||||
expect(project).to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue