Commit edab9360 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into 'rd-44364-deprecate-support-for-dsa-keys'

# Conflicts:
#   db/schema.rb
parents c503429e 6ced2f12
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.7-golang-1.9-git-2.17-chrome-65.0-node-8.x-yarn-1.2-postgresql-9.6"
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-65.0-node-8.x-yarn-1.2-postgresql-9.6"
.dedicated-runner: &dedicated-runner
retry: 1
......@@ -6,7 +6,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.7-golang-1.9-git
- gitlab-org
.default-cache: &default-cache
key: "ruby-2.3.7-debian-stretch-with-yarn"
key: "ruby-2.4.4-debian-stretch-with-yarn"
paths:
- vendor/ruby
- .yarn-cache/
......@@ -550,7 +550,7 @@ static-analysis:
script:
- scripts/static-analysis
cache:
key: "ruby-2.3.7-debian-stretch-with-yarn-and-rubocop"
key: "ruby-2.4.4-debian-stretch-with-yarn-and-rubocop"
paths:
- vendor/ruby
- .yarn-cache/
......
......@@ -182,7 +182,7 @@ Assigning a team label makes sure issues get the attention of the appropriate
people.
The current team labels are ~Distribution, ~"CI/CD", ~Discussion, ~Documentation, ~Quality,
~Geo, ~Gitaly, ~Monitoring, ~Platform, ~Release, ~"Security Products" and ~"UX".
~Geo, ~Gitaly, ~Monitoring, ~Platform, ~Release, ~"Security Products", ~"Configuration", and ~"UX".
The descriptions on the [labels page][labels-page] explain what falls under the
responsibility of each team.
......
......@@ -342,7 +342,7 @@ group :development, :test do
gem 'capybara', '~> 2.15'
gem 'capybara-screenshot', '~> 1.0.0'
gem 'selenium-webdriver', '~> 3.5'
gem 'selenium-webdriver', '~> 3.12'
gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4'
......@@ -374,7 +374,7 @@ end
group :test do
gem 'shoulda-matchers', '~> 3.1.2', require: false
gem 'email_spec', '~> 1.6.0'
gem 'email_spec', '~> 2.2.0'
gem 'json-schema', '~> 2.8.0'
gem 'webmock', '~> 2.3.2'
gem 'rails-controller-testing' if rails5? # Rails5 only gem.
......@@ -384,7 +384,7 @@ group :test do
gem 'test-prof', '~> 0.2.5'
end
gem 'octokit', '~> 4.8'
gem 'octokit', '~> 4.9'
gem 'mail_room', '~> 0.9.1'
......
......@@ -115,7 +115,7 @@ GEM
mime-types (>= 1.16)
cause (0.1)
charlock_holmes (0.7.6)
childprocess (0.7.0)
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
chronic (0.10.2)
chronic_duration (0.10.6)
......@@ -172,15 +172,16 @@ GEM
unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.3.2)
railties (>= 4.2)
doorkeeper-openid_connect (1.3.0)
doorkeeper-openid_connect (1.4.0)
doorkeeper (~> 4.3)
json-jwt (~> 1.6)
dropzonejs-rails (0.7.2)
rails (> 3.1)
email_reply_trimmer (0.1.6)
email_spec (1.6.0)
email_spec (2.2.0)
htmlentities (~> 4.3.3)
launchy (~> 2.1)
mail (~> 2.2)
mail (~> 2.7)
encryptor (3.0.0)
equalizer (0.0.11)
erubis (2.7.0)
......@@ -517,7 +518,7 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
octokit (4.8.0)
octokit (4.9.0)
sawyer (~> 0.8.0, >= 0.5.3)
omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0)
......@@ -828,9 +829,9 @@ GEM
activesupport (>= 3.1)
select2-rails (3.5.9.3)
thor (~> 0.14)
selenium-webdriver (3.5.0)
selenium-webdriver (3.12.0)
childprocess (~> 0.5)
rubyzip (~> 1.0)
rubyzip (~> 1.2)
sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
......@@ -1012,7 +1013,7 @@ DEPENDENCIES
doorkeeper-openid_connect (~> 1.3)
dropzonejs-rails (~> 0.7.1)
email_reply_trimmer (~> 0.1)
email_spec (~> 1.6.0)
email_spec (~> 2.2.0)
factory_bot_rails (~> 4.8.2)
faraday (~> 0.12)
fast_blank
......@@ -1084,7 +1085,7 @@ DEPENDENCIES
net-ssh (~> 4.2.0)
nokogiri (~> 1.8.2)
oauth2 (~> 1.4)
octokit (~> 4.8)
octokit (~> 4.9)
omniauth (~> 1.8)
omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.3)
......@@ -1154,7 +1155,7 @@ DEPENDENCIES
scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7)
select2-rails (~> 3.5.9)
selenium-webdriver (~> 3.5)
selenium-webdriver (~> 3.12)
sentry-raven (~> 2.7)
settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6)
......
......@@ -72,8 +72,6 @@ GEM
attr_encrypted (3.1.0)
encryptor (~> 3.0.0)
attr_required (1.0.1)
autoprefixer-rails (8.1.0.1)
execjs
awesome_print (1.2.0)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
......@@ -93,9 +91,6 @@ GEM
binding_of_caller (0.7.3)
debug_inspector (>= 0.0.1)
blankslate (2.1.2.4)
bootstrap-sass (3.3.7)
autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4)
bootstrap_form (2.7.0)
brakeman (4.2.1)
browser (2.5.3)
......@@ -175,7 +170,7 @@ GEM
diff-lcs (1.3)
diffy (3.1.0)
docile (1.1.5)
domain_name (0.5.20170404)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.3.1)
railties (>= 4.2)
......@@ -185,9 +180,10 @@ GEM
dropzonejs-rails (0.7.4)
rails (> 3.1)
email_reply_trimmer (0.1.10)
email_spec (1.6.0)
email_spec (2.2.0)
htmlentities (~> 4.3.3)
launchy (~> 2.1)
mail (~> 2.2)
mail (~> 2.7)
encryptor (3.0.0)
equalizer (0.0.11)
erubis (2.7.0)
......@@ -288,7 +284,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (0.99.0)
gitaly-proto (0.100.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
......@@ -365,9 +361,9 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
grape-route-helpers (2.1.0)
grape-path-helpers (1.0.0)
activesupport
grape (>= 0.16.0)
grape (~> 1.0)
rake
grape_logging (1.7.0)
grape
......@@ -417,6 +413,7 @@ GEM
httpclient (2.8.3)
i18n (1.0.1)
concurrent-ruby (~> 1.0)
icalendar (2.4.1)
ice_nine (0.11.2)
influxdb (0.5.3)
ipaddress (0.8.3)
......@@ -450,9 +447,9 @@ GEM
kgio (2.11.2)
knapsack (1.16.0)
rake
kubeclient (3.0.0)
kubeclient (3.1.1)
http (~> 2.2.2)
recursive-open-struct (~> 1.0.4)
recursive-open-struct (~> 1.0, >= 1.0.4)
rest-client (~> 2.0)
launchy (2.4.3)
addressable (~> 2.3)
......@@ -521,15 +518,16 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
octokit (4.8.0)
octokit (4.9.0)
sawyer (~> 0.8.0, >= 0.5.3)
omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.6.2, < 3)
omniauth-auth0 (2.0.0)
omniauth-oauth2 (~> 1.4)
omniauth-authentiq (0.3.1)
omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-authentiq (0.3.3)
jwt (>= 1.5)
omniauth-oauth2 (>= 1.5)
omniauth-azure-oauth2 (0.0.9)
jwt (~> 1.0)
omniauth (~> 1.0)
......@@ -628,7 +626,7 @@ GEM
parser
unparser
procto (0.0.3)
prometheus-client-mmap (0.9.2)
prometheus-client-mmap (0.9.3)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
......@@ -702,11 +700,11 @@ GEM
ffi
rbnacl-libsodium (1.0.16)
rbnacl (>= 3.0.1)
rdoc (4.3.0)
rdoc (6.0.4)
re2 (1.1.1)
recaptcha (3.4.0)
json
recursive-open-struct (1.0.5)
recursive-open-struct (1.1.0)
redcarpet (3.4.0)
redis (3.3.5)
redis-actionpack (5.0.2)
......@@ -716,8 +714,8 @@ GEM
redis-activesupport (5.0.4)
activesupport (>= 3, < 6)
redis-store (>= 1.3, < 2)
redis-namespace (1.5.3)
redis (~> 3.0, >= 3.0.4)
redis-namespace (1.6.0)
redis (>= 3.0.4)
redis-rack (2.0.4)
rack (>= 1.5, < 3)
redis-store (>= 1.2, < 2)
......@@ -836,7 +834,7 @@ GEM
activesupport (>= 3.1)
select2-rails (3.5.10)
thor (~> 0.14)
selenium-webdriver (3.11.0)
selenium-webdriver (3.12.0)
childprocess (~> 0.5)
rubyzip (~> 1.2)
sentry-raven (2.7.2)
......@@ -986,7 +984,7 @@ DEPENDENCIES
asciidoctor-plantuml (= 0.0.8)
asset_sync (~> 2.4)
attr_encrypted (~> 3.1.0)
awesome_print (~> 1.2.0)
awesome_print
babosa (~> 1.0.2)
base32 (~> 0.3.0)
batch-loader (~> 1.2.1)
......@@ -994,7 +992,6 @@ DEPENDENCIES
benchmark-ips (~> 2.3.0)
better_errors (~> 2.1.0)
binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0)
bootstrap_form (~> 2.7.0)
brakeman (~> 4.2)
browser (~> 2.2)
......@@ -1021,7 +1018,7 @@ DEPENDENCIES
doorkeeper-openid_connect (~> 1.3)
dropzonejs-rails (~> 0.7.1)
email_reply_trimmer (~> 0.1)
email_spec (~> 1.6.0)
email_spec (~> 2.2.0)
factory_bot_rails (~> 4.8.2)
faraday (~> 0.12)
fast_blank
......@@ -1045,7 +1042,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.99.0)
gitaly-proto (~> 0.100.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
......@@ -1059,7 +1056,7 @@ DEPENDENCIES
gpgme
grape (~> 1.0)
grape-entity (~> 0.7.1)
grape-route-helpers (~> 2.1.0)
grape-path-helpers (~> 1.0)
grape_logging (~> 1.7)
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
......@@ -1070,6 +1067,7 @@ DEPENDENCIES
html-pipeline (~> 2.7.1)
html2text
httparty (~> 0.13.3)
icalendar
influxdb (~> 0.2)
jira-ruby (~> 1.4)
jquery-atwho-rails (~> 1.3.2)
......@@ -1077,7 +1075,7 @@ DEPENDENCIES
jwt (~> 1.5.6)
kaminari (~> 1.0)
knapsack (~> 1.16)
kubeclient (~> 3.0)
kubeclient (~> 3.1.0)
letter_opener_web (~> 1.3.0)
license_finder (~> 3.1)
licensee (~> 8.9)
......@@ -1092,10 +1090,10 @@ DEPENDENCIES
net-ssh (~> 4.2.0)
nokogiri (~> 1.8.2)
oauth2 (~> 1.4)
octokit (~> 4.8)
octokit (~> 4.9)
omniauth (~> 1.8)
omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.1)
omniauth-authentiq (~> 0.3.3)
omniauth-azure-oauth2 (~> 0.0.9)
omniauth-cas3 (~> 1.1.4)
omniauth-facebook (~> 4.0.0)
......@@ -1118,7 +1116,7 @@ DEPENDENCIES
peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2)
premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.9.2)
prometheus-client-mmap (~> 0.9.3)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1)
......@@ -1134,12 +1132,12 @@ DEPENDENCIES
rblineprof (~> 0.3.6)
rbnacl (~> 4.0)
rbnacl-libsodium
rdoc (~> 4.2)
rdoc (~> 6.0)
re2 (~> 1.1.1)
recaptcha (~> 3.0)
redcarpet (~> 3.4)
redis (~> 3.2)
redis-namespace (~> 1.5.2)
redis-namespace (~> 1.6.0)
redis-rails (~> 5.0.2)
request_store (~> 1.3)
responders (~> 2.0)
......@@ -1154,6 +1152,7 @@ DEPENDENCIES
rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.17.0)
ruby-progressbar
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.27)
......@@ -1162,12 +1161,12 @@ DEPENDENCIES
scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7)
select2-rails (~> 3.5.9)
selenium-webdriver (~> 3.5)
selenium-webdriver (~> 3.12)
sentry-raven (~> 2.7)
settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6)
shoulda-matchers (~> 3.1.2)
sidekiq (~> 5.0)
sidekiq (~> 5.1)
sidekiq-cron (~> 0.6.0)
sidekiq-limit_fetch (~> 3.4)
simple_po_parser (~> 1.1.2)
......@@ -1199,4 +1198,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.16.1
1.16.2
......@@ -126,5 +126,5 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on
## Is it awesome?
Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua.
[These people](https://twitter.com/gitlab/likes) seem to like it.
......@@ -187,7 +187,7 @@
role="row"
>
<div
class="alert alert-danger alert-block append-bottom-0"
class="alert alert-danger alert-block append-bottom-0 clusters-error-alert"
role="gridcell"
>
<div>
......
<script>
import $ from 'jquery';
import { mapActions, mapGetters, mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
......@@ -20,6 +21,13 @@ export default {
},
methods: {
...mapActions(['updateActivityBarView']),
changedActivityView(e, view) {
e.currentTarget.blur();
this.updateActivityBarView(view);
$(e.currentTarget).tooltip('hide');
},
},
activityBarViews,
};
......@@ -54,7 +62,7 @@ export default {
:class="{
active: currentActivityView === $options.activityBarViews.edit
}"
@click.prevent="updateActivityBarView($options.activityBarViews.edit)"
@click.prevent="changedActivityView($event, $options.activityBarViews.edit)"
:title="s__('IDE|Edit')"
:aria-label="s__('IDE|Edit')"
>
......@@ -73,7 +81,7 @@ export default {
:class="{
active: currentActivityView === $options.activityBarViews.review
}"
@click.prevent="updateActivityBarView($options.activityBarViews.review)"
@click.prevent="changedActivityView($event, $options.activityBarViews.review)"
:title="s__('IDE|Review')"
:aria-label="s__('IDE|Review')"
>
......@@ -92,7 +100,7 @@ export default {
:class="{
active: currentActivityView === $options.activityBarViews.commit
}"
@click.prevent="updateActivityBarView($options.activityBarViews.commit)"
@click.prevent="changedActivityView($event, $options.activityBarViews.commit)"
:title="s__('IDE|Commit')"
:aria-label="s__('IDE|Commit')"
>
......
......@@ -54,7 +54,7 @@ export default {
placement: 'top',
content: sprintf(
__(`
The character highligher helps you keep the subject line to %{titleLength} characters
The character highlighter helps you keep the subject line to %{titleLength} characters
and wrap the body at %{bodyLength} so they are readable in git.
`),
{ titleLength: MAX_TITLE_LENGTH, bodyLength: MAX_BODY_LENGTH },
......
......@@ -426,7 +426,7 @@ export default class LabelsSelect {
const tpl = _.template([
'<% _.each(labels, function(label){ %>',
'<a href="<%- issueUpdateURL.slice(0, issueUpdateURL.lastIndexOf("/")) %>?label_name[]=<%- encodeURIComponent(label.title) %>">',
'<span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">',
'<span class="badge label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">',
'<%- label.title %>',
'</span>',
'</a>',
......
......@@ -14,6 +14,7 @@ export const EPIC_NOTEABLE_TYPE = 'epic';
export const MERGE_REQUEST_NOTEABLE_TYPE = 'merge_request';
export const UNRESOLVE_NOTE_METHOD_NAME = 'delete';
export const RESOLVE_NOTE_METHOD_NAME = 'post';
export const DESCRIPTION_TYPE = 'changed the description';
export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE,
......
import { n__, s__, sprintf } from '~/locale';
import { DESCRIPTION_TYPE } from '../constants';
/**
* Changes the description from a note, returns 'changed the description n number of times'
*/
export const changeDescriptionNote = (note, descriptionChangedTimes, timeDifferenceMinutes) => {
const descriptionNote = Object.assign({}, note);
descriptionNote.note_html = sprintf(
s__(`MergeRequest|
%{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}`),
{
paragraphStart: '<p dir="auto">',
paragraphEnd: '</p>',
descriptionChangedTimes,
timeDifferenceMinutes: n__('within %d minute ', 'within %d minutes ', timeDifferenceMinutes),
},
false,
);
descriptionNote.times_updated = descriptionChangedTimes;
return descriptionNote;
};
/**
* Checks the time difference between two notes from their 'created_at' dates
* returns an integer
*/
export const getTimeDifferenceMinutes = (noteBeggining, noteEnd) => {
const descriptionNoteBegin = new Date(noteBeggining.created_at);
const descriptionNoteEnd = new Date(noteEnd.created_at);
const timeDifferenceMinutes = (descriptionNoteEnd - descriptionNoteBegin) / 1000 / 60;
return Math.ceil(timeDifferenceMinutes);
};
/**
* Checks if a note is a system note and if the content is description
*
* @param {Object} note
* @returns {Boolean}
*/
export const isDescriptionSystemNote = note => note.system && note.note === DESCRIPTION_TYPE;
/**
* Collapses the system notes of a description type, e.g. Changed the description, n minutes ago
* the notes will collapse as long as they happen no more than 10 minutes away from each away
* in between the notes can be anything, another type of system note
* (such as 'changed the weight') or a comment.
*
* @param {Array} notes
* @returns {Array}
*/
export const collapseSystemNotes = notes => {
let lastDescriptionSystemNote = null;
let lastDescriptionSystemNoteIndex = -1;
let descriptionChangedTimes = 1;
return notes.slice(0).reduce((acc, currentNote) => {
const note = currentNote.notes[0];
if (isDescriptionSystemNote(note)) {
// is it the first one?
if (!lastDescriptionSystemNote) {
lastDescriptionSystemNote = note;
lastDescriptionSystemNoteIndex = acc.length;
} else if (lastDescriptionSystemNote) {
const timeDifferenceMinutes = getTimeDifferenceMinutes(
lastDescriptionSystemNote,
note,
);
// are they less than 10 minutes appart?
if (timeDifferenceMinutes > 10) {
// reset counter
descriptionChangedTimes = 1;
// update the previous system note
lastDescriptionSystemNote = note;
lastDescriptionSystemNoteIndex = acc.length;
} else {
// increase counter
descriptionChangedTimes += 1;
// delete the previous one
acc.splice(lastDescriptionSystemNoteIndex, 1);
// replace the text of the current system note with the collapsed note.
currentNote.notes.splice(
0,
1,
changeDescriptionNote(note, descriptionChangedTimes, timeDifferenceMinutes),
);
// update the previous system note index
lastDescriptionSystemNoteIndex = acc.length;
}
}
}
acc.push(currentNote);
return acc;
}, []);
};
// for babel-rewire
export default {};
import _ from 'underscore';
import { collapseSystemNotes } from './collapse_utils';
export const notes = state => collapseSystemNotes(state.notes);
export const notes = state => state.notes;
export const targetNoteHash = state => state.targetNoteHash;
export const getNotesData = state => state.notesData;
......
......@@ -265,10 +265,10 @@ export default {
/>
<section
v-if="mr.maintainerEditAllowed"
v-if="mr.allowCollaboration"
class="mr-info-list mr-links"
>
{{ s__("mrWidget|Allows edits from maintainers") }}
{{ s__("mrWidget|Allows commits from members who can merge to the target branch") }}
</section>
<mr-widget-related-links
......
......@@ -83,7 +83,7 @@ export default class MergeRequestStore {
this.canBeMerged = data.can_be_merged || false;
this.isMergeAllowed = data.mergeable || false;
this.mergeOngoing = data.merge_ongoing;
this.maintainerEditAllowed = data.allow_maintainer_to_push;
this.allowCollaboration = data.allow_collaboration;
// Cherry-pick and Revert actions related
this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
......
......@@ -35,7 +35,12 @@ export default {
</script>
<template>
<div class="hide-collapsed value issuable-show-labels js-value">
<div
class="hide-collapsed value issuable-show-labels js-value"
:class="{
'has-labels':!isEmpty,
}"
>
<span
v-if="isEmpty"
class="text-secondary"
......@@ -50,7 +55,7 @@ export default {
>
<span
v-tooltip
class="label color-label"
class="badge color-label"
data-placement="bottom"
data-container="body"
:style="labelStyle(label)"
......
......@@ -36,6 +36,12 @@ html [type="button"],
cursor: pointer;
}
input[type="file"] {
// Bootstrap 4 file input height is taller by default
// which makes them look ugly
line-height: 1;
}
b,
strong {
font-weight: bold;
......@@ -55,8 +61,23 @@ a {
code {
padding: 2px 4px;
color: $red-600;
background-color: $red-100;
border-radius: 3px;
.code & {
background-color: inherit;
padding: unset;
}
.build-trace & {
background-color: inherit;
padding: inherit;
}
}
.code {
padding: 9.5px;
}
table {
......@@ -162,7 +183,9 @@ table {
.nav-tabs {
.nav-link {
border: 0;
border-top: 0;
border-left: 0;
border-right: 0;
}
.nav-item {
......
......@@ -35,6 +35,12 @@
@include media-breakpoint-down(xs) {
width: 100%;
}
&.projects-dropdown-menu {
padding: 0;
overflow-y: initial;
max-height: initial;
}
}
.dropdown-toggle,
......
......@@ -35,7 +35,7 @@
}
&.active > a,
&.dropdown.open > a {
&.dropdown.show > a {
color: $color-900;
background-color: $color-alternate;
}
......@@ -74,7 +74,7 @@
}
&.active > a,
&.dropdown.open > a {
&.dropdown.show > a {
color: $color-900;
background-color: $color-alternate;
......
......@@ -297,12 +297,6 @@
display: flex;
margin: 0 0 0 6px;
.projects-dropdown-menu {
padding: 0;
overflow-y: initial;
max-height: initial;
}
.dropdown-chevron {
position: relative;
top: -1px;
......
......@@ -115,9 +115,3 @@ body {
.with-performance-bar .layout-page {
margin-top: $header-height + $performance-bar-height;
}
.vertical-center {
min-height: 100vh;
display: flex;
align-items: center;
}
......@@ -11,15 +11,15 @@
padding-top: $gl-padding;
}
.panel {
.panel-heading {
.card {
.card-header {
display: -webkit-flex;
display: flex;
align-items: center;
justify-content: space-between;
line-height: $line-height-base;
.title {
.card-title {
display: flex;
align-items: center;
......@@ -34,6 +34,8 @@
.navbar-collapse {
padding-right: 0;
flex-grow: 0;
flex-basis: auto;
.navbar-nav {
margin: 0;
......
......@@ -114,26 +114,27 @@
font-size: 0.95em;
}
blockquote,
.blockquote {
color: $gl-grayish-blue;
font-size: inherit;
padding: 8px 24px;
margin: 16px 0;
border-left: 3px solid $white-dark;
}
.blockquote:dir(rtl) {
border-left: 0;
border-right: 3px solid $white-dark;
}
&:dir(rtl) {
border-left: 0;
border-right: 3px solid $white-dark;
}
.blockquote p {
color: $gl-grayish-blue !important;
font-size: inherit;
line-height: 1.5;
p {
color: $gl-grayish-blue !important;
font-size: inherit;
line-height: 1.5;
&:last-child {
margin: 0;
&:last-child {
margin: 0;
}
}
}
......
......@@ -138,6 +138,7 @@ pre {
margin: 0;
}
blockquote,
.blockquote {
color: $gl-grayish-blue;
padding: 0 0 0 15px;
......
......@@ -13,6 +13,10 @@
max-width: 100%;
}
.clusters-error-alert {
width: 100%;
}
.clusters-container {
.nav-bar-right {
padding: $gl-padding-top $gl-padding;
......
......@@ -196,6 +196,10 @@
.prioritized-labels {
margin-bottom: 30px;
h5 {
font-size: $gl-font-size;
}
.add-priority {
display: none;
color: $gray-light;
......@@ -210,6 +214,10 @@
}
.other-labels {
h5 {
font-size: $gl-font-size;
}
.remove-priority {
display: none;
}
......
......@@ -183,7 +183,7 @@
svg {
position: relative;
top: -1px;
top: -2px;
}
.ide-file-changed-icon {
......@@ -458,6 +458,10 @@
width: auto;
margin-right: 0;
a {
height: 60px;
}
a:hover,
a:focus {
text-decoration: none;
......@@ -718,9 +722,17 @@
}
.ide-new-btn {
.btn {
padding-top: 3px;
padding-bottom: 3px;
}
.dropdown {
display: flex;
}
.dropdown-toggle svg {
margin-top: -2px;
margin-bottom: 2px;
top: 0;
}
.dropdown-menu {
......@@ -877,6 +889,7 @@
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
outline: 0;
cursor: pointer;
svg {
margin: 0 auto;
......
......@@ -174,7 +174,7 @@
.option-description,
.option-disabled-reason {
margin-left: 45px;
margin-left: 30px;
color: $project-option-descr-color;
}
......
......@@ -22,9 +22,9 @@
header,
nav,
nav.main-nav,
nav.navbar-collapse,
nav.navbar-collapse.collapse,
.nav-sidebar,
.profiler-results,
.tree-ref-holder,
.tree-holder .breadcrumb,
......@@ -38,7 +38,8 @@ ul.notes-form,
.edit-link,
.note-action-button,
.right-sidebar,
.flash-container {
.flash-container,
#js-peek {
display: none !important;
}
......
......@@ -24,7 +24,9 @@ module Groups
# Make the `search` param consistent for the frontend,
# which will be using `filter`.
params[:search] ||= params[:filter] if params[:filter]
params.permit(:sort, :search)
# Don't show archived projects
params[:non_archived] = true
params.permit(:sort, :search, :non_archived)
end
end
end
......
......@@ -18,7 +18,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
def upload_authorize
set_workhorse_internal_api_content_type
authorized = LfsObjectUploader.workhorse_authorize
authorized = LfsObjectUploader.workhorse_authorize(has_length: true)
authorized.merge!(LfsOid: oid, LfsSize: size)
render json: authorized
......
......@@ -15,7 +15,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
def merge_request_params_attributes
[
:allow_maintainer_to_push,
:allow_collaboration,
:assignee_id,
:description,
:force_remove_source_branch,
......
class Projects::MilestonesController < Projects::ApplicationController
include Gitlab::Utils::StrongMemoize
include MilestoneActions
before_action :check_issuables_available!
......@@ -103,7 +104,7 @@ class Projects::MilestonesController < Projects::ApplicationController
protected
def milestones
@milestones ||= begin
strong_memoize(:milestones) do
MilestonesFinder.new(search_params).execute
end
end
......@@ -121,10 +122,10 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def search_params
if @project.group && can?(current_user, :read_group, @project.group)
group = @project.group
if request.format.json? && @project.group && can?(current_user, :read_group, @project.group)
groups = @project.group.self_and_ancestors
end
params.permit(:state).merge(project_ids: @project.id, group_ids: group&.id)
params.permit(:state).merge(project_ids: @project.id, group_ids: groups&.select(:id))
end
end
......@@ -36,7 +36,7 @@ module ApplicationSettingsHelper
# Return a group of checkboxes that use Bootstrap's button plugin for a
# toggle button effect.
def restricted_level_checkboxes(help_block_id, checkbox_name)
def restricted_level_checkboxes(help_block_id, checkbox_name, options = {})
Gitlab::VisibilityLevel.values.map do |level|
checked = restricted_visibility_levels(true).include?(level)
css_class = checked ? 'active' : ''
......@@ -46,6 +46,7 @@ module ApplicationSettingsHelper
check_box_tag(checkbox_name, level, checked,
autocomplete: 'off',
'aria-describedby' => help_block_id,
'class' => options[:class],
id: tag_name) + visibility_level_icon(level) + visibility_level_label(level)
end
end
......@@ -53,7 +54,7 @@ module ApplicationSettingsHelper
# Return a group of checkboxes that use Bootstrap's button plugin for a
# toggle button effect.
def import_sources_checkboxes(help_block_id)
def import_sources_checkboxes(help_block_id, options = {})
Gitlab::ImportSources.options.map do |name, source|
checked = Gitlab::CurrentSettings.import_sources.include?(source)
css_class = checked ? 'active' : ''
......@@ -63,6 +64,7 @@ module ApplicationSettingsHelper
check_box_tag(checkbox_name, source, checked,
autocomplete: 'off',
'aria-describedby' => help_block_id,
'class' => options[:class],
id: name.tr(' ', '_')) + name
end
end
......
......@@ -126,8 +126,8 @@ module MergeRequestsHelper
link_to(url[merge_request.project, merge_request], data: data_attrs, &block)
end
def allow_maintainer_push_unavailable_reason(merge_request)
return if merge_request.can_allow_maintainer_to_push?(current_user)
def allow_collaboration_unavailable_reason(merge_request)
return if merge_request.can_allow_collaboration?(current_user)
minimum_visibility = [merge_request.target_project.visibility_level,
merge_request.source_project.visibility_level].min
......
......@@ -238,6 +238,14 @@ module ProjectsHelper
"git push --set-upstream #{repository_url}/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)"
end
def show_xcode_link?(project = @project)
browser.platform.mac? && project.repository.xcode_project?
end
def xcode_uri_to_repo(project = @project)
"xcode://clone?repo=#{CGI.escape(default_url_to_repo(project))}"
end
private
def get_project_nav_tabs(project, current_user)
......
......@@ -25,14 +25,22 @@ module SearchHelper
return unless collection.count > 0
from = collection.offset_value + 1
to = collection.offset_value + collection.length
to = collection.offset_value + collection.count
count = collection.total_count
"Showing #{from} - #{to} of #{count} #{scope.humanize(capitalize: false)} for \"#{term}\""
end
def find_project_for_result_blob(result)
@project
end
def parse_search_result(result)
Gitlab::ProjectSearchResults.parse_search_result(result)
result
end
def search_blob_title(project, filename)
filename
end
private
......
......@@ -55,6 +55,11 @@ module Ci
where('(artifacts_file IS NOT NULL AND artifacts_file <> ?) OR EXISTS (?)',
'', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').archive)
end
scope :without_archived_trace, ->() do
where('NOT EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').trace)
end
scope :with_artifacts_stored_locally, -> { with_artifacts_archive.where(artifacts_file_store: [nil, LegacyArtifactUploader::Store::LOCAL]) }
scope :with_artifacts_not_expired, ->() { with_artifacts_archive.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts_archive.where('artifacts_expire_at < ?', Time.now) }
......@@ -144,6 +149,7 @@ module Ci
after_transition any => [:success] do |build|
build.run_after_commit do
BuildSuccessWorker.perform_async(id)
PagesWorker.perform_async(:deploy, id) if build.pages_generator?
end
end
......@@ -183,6 +189,11 @@ module Ci
pipeline.manual_actions.where.not(name: name)
end
def pages_generator?
Gitlab.config.pages.enabled &&
self.name == 'pages'
end
def playable?
action? && (manual? || retryable?)
end
......@@ -402,8 +413,6 @@ module Ci
build_data = Gitlab::DataBuilder::Build.build(self)
project.execute_hooks(build_data.dup, :job_hooks)
project.execute_services(build_data.dup, :job_hooks)
PagesService.new(build_data).execute
project.running_or_pending_build_count(force: true)
end
def browsable_artifacts?
......
......@@ -7,12 +7,17 @@ module Ci
include Presentable
include Gitlab::OptimisticLocking
include Gitlab::Utils::StrongMemoize
include AtomicInternalId
belongs_to :project, inverse_of: :pipelines
belongs_to :user
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
has_internal_id :iid, scope: :project, presence: false, init: ->(s) do
s&.project&.pipelines&.maximum(:iid) || s&.project&.pipelines&.count
end
has_many :stages
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
......@@ -531,6 +536,7 @@ module Ci
def predefined_variables
Gitlab::Ci::Variables::Collection.new
.append(key: 'CI_PIPELINE_IID', value: iid.to_s)
.append(key: 'CI_CONFIG_PATH', value: ci_yaml_file_path)
.append(key: 'CI_PIPELINE_SOURCE', value: source.to_s)
.append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message)
......
......@@ -219,10 +219,8 @@ module Ci
cache_attributes(values)
if persist_cached_data?
self.assign_attributes(values)
self.save if self.changed?
end
# We save data without validation, it will always change due to `contacted_at`
self.update_columns(values) if persist_cached_data?
end
def pick_build!(build)
......
......@@ -11,12 +11,12 @@ module Clusters
attr_encrypted :password,
mode: :per_attribute_iv,
key: Gitlab::Application.secrets.db_key_base,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-cbc'
attr_encrypted :token,
mode: :per_attribute_iv,
key: Gitlab::Application.secrets.db_key_base,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-cbc'
before_validation :enforce_namespace_to_lower_case
......
......@@ -11,7 +11,7 @@ module Clusters
attr_encrypted :access_token,
mode: :per_attribute_iv,
key: Gitlab::Application.secrets.db_key_base,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-cbc'
validates :gcp_project_id,
......
......@@ -25,9 +25,13 @@ module AtomicInternalId
extend ActiveSupport::Concern
module ClassMethods
def has_internal_id(column, scope:, init:) # rubocop:disable Naming/PredicateName
before_validation(on: :create) do
def has_internal_id(column, scope:, init:, presence: true) # rubocop:disable Naming/PredicateName
before_validation :"ensure_#{scope}_#{column}!", on: :create
validates column, presence: presence
define_method("ensure_#{scope}_#{column}!") do
scope_value = association(scope).reader
if read_attribute(column).blank? && scope_value
scope_attrs = { scope_value.class.table_name.singularize.to_sym => scope_value }
usage = self.class.table_name.to_sym
......@@ -35,13 +39,9 @@ module AtomicInternalId
new_iid = InternalId.generate_next(self, scope_attrs, usage, init)
write_attribute(column, new_iid)
end
end
validates column, presence: true, numericality: true
read_attribute(column)
end
end
end
def to_param
iid.to_s
end
end
......@@ -4,11 +4,14 @@ module Avatarable
included do
prepend ShadowMethods
include ObjectStorage::BackgroundMove
include Gitlab::Utils::StrongMemoize
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
mount_uploader :avatar, AvatarUploader
after_initialize :add_avatar_to_batch
end
module ShadowMethods
......@@ -18,6 +21,17 @@ module Avatarable
avatar_path(only_path: args.fetch(:only_path, true)) || super
end
def retrieve_upload(identifier, paths)
upload = retrieve_upload_from_batch(identifier)
# This fallback is needed when deleting an upload, because we may have
# already been removed from the DB. We have to check an explicit `#nil?`
# because it's a BatchLoader instance.
upload = super if upload.nil?
upload
end
end
def avatar_type
......@@ -52,4 +66,37 @@ module Avatarable
url_base + avatar.local_url
end
# Path that is persisted in the tracking Upload model. Used to fetch the
# upload from the model.
def upload_paths(identifier)
avatar_mounter.blank_uploader.store_dirs.map { |store, path| File.join(path, identifier) }
end
private
def retrieve_upload_from_batch(identifier)
BatchLoader.for(identifier: identifier, model: self).batch(key: self.class) do |upload_params, loader, args|
model_class = args[:key]
paths = upload_params.flat_map do |params|
params[:model].upload_paths(params[:identifier])
end
Upload.where(uploader: AvatarUploader, path: paths).find_each do |upload|
model = model_class.instantiate('id' => upload.model_id)
loader.call({ model: model, identifier: File.basename(upload.path) }, upload)
end
end
end
def add_avatar_to_batch
return unless avatar_mounter
avatar_mounter.read_identifiers.each { |identifier| retrieve_upload_from_batch(identifier) }
end
def avatar_mounter
strong_memoize(:avatar_mounter) { _mounter(:avatar) }
end
end
......@@ -13,7 +13,7 @@ module HasVariable
attr_encrypted :value,
mode: :per_attribute_iv_and_salt,
insecure_mode: true,
key: Gitlab::Application.secrets.db_key_base,
key: Settings.attr_encrypted_db_key_base,
algorithm: 'aes-256-cbc'
def key=(new_key)
......
module IidRoutes
##
# This automagically enforces all related routes to use `iid` instead of `id`
# If you want to use `iid` for some routes and `id` for other routes, this module should not to be included,
# instead you should define `iid` or `id` explictly at each route generators. e.g. pipeline_path(project.id, pipeline.iid)
def to_param
iid.to_s
end
end
......@@ -36,4 +36,8 @@ module WithUploads
upload.destroy
end
end
def retrieve_upload(_identifier, paths)
uploads.find_by(path: paths)
end
end
class Deployment < ActiveRecord::Base
include AtomicInternalId
include IidRoutes
belongs_to :project, required: true
belongs_to :environment, required: true
......
......@@ -14,7 +14,7 @@ class InternalId < ActiveRecord::Base
belongs_to :project
belongs_to :namespace
enum usage: { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4 }
enum usage: { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4, ci_pipelines: 5 }
validates :usage, presence: true
......
......@@ -2,6 +2,7 @@ require 'carrierwave/orm/activerecord'
class Issue < ActiveRecord::Base
include AtomicInternalId
include IidRoutes
include Issuable
include Noteable
include Referable
......
class MergeRequest < ActiveRecord::Base
include AtomicInternalId
include IidRoutes
include Issuable
include Noteable
include Referable
......@@ -1124,21 +1125,21 @@ class MergeRequest < ActiveRecord::Base
project.merge_requests.merged.where(author_id: author_id).empty?
end
def allow_maintainer_to_push
maintainer_push_possible? && super
def allow_collaboration
collaborative_push_possible? && super
end
alias_method :allow_maintainer_to_push?, :allow_maintainer_to_push
alias_method :allow_collaboration?, :allow_collaboration
def maintainer_push_possible?
def collaborative_push_possible?
source_project.present? && for_fork? &&
target_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE &&
source_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE &&
!ProtectedBranch.protected?(source_project, source_branch)
end
def can_allow_maintainer_to_push?(user)
maintainer_push_possible? &&
def can_allow_collaboration?(user)
collaborative_push_possible? &&
Ability.allowed?(user, :push_code, source_project)
end
......
......@@ -9,6 +9,7 @@ class Milestone < ActiveRecord::Base
include CacheMarkdownField
include AtomicInternalId
include IidRoutes
include Sortable
include Referable
include StripAttribute
......
......@@ -435,6 +435,10 @@ class Note < ActiveRecord::Base
super.merge(noteable: noteable)
end
def retrieve_upload(_identifier, paths)
Upload.find_by(model: self, path: paths)
end
private
def keep_around_commit
......
......@@ -19,7 +19,7 @@ class PagesDomain < ActiveRecord::Base
attr_encrypted :key,
mode: :per_attribute_iv_and_salt,
insecure_mode: true,
key: Gitlab::Application.secrets.db_key_base,
key: Settings.attr_encrypted_db_key_base,
algorithm: 'aes-256-cbc'
after_initialize :set_verification_code
......
class PersonalSnippet < Snippet
include WithUploads
end
......@@ -1649,12 +1649,6 @@ class Project < ActiveRecord::Base
import_state.update_column(:jid, nil)
end
def running_or_pending_build_count(force: false)
Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do
builds.running_or_pending.count(:all)
end
end
# Lazy loading of the `pipeline_status` attribute
def pipeline_status
@pipeline_status ||= Gitlab::Cache::Ci::ProjectPipelineStatus.load_for_project(self)
......@@ -1974,18 +1968,18 @@ class Project < ActiveRecord::Base
.limit(1)
.select(1)
source_of_merge_requests.opened
.where(allow_maintainer_to_push: true)
.where(allow_collaboration: true)
.where('EXISTS (?)', developer_access_exists)
end
def branch_allows_maintainer_push?(user, branch_name)
def branch_allows_collaboration?(user, branch_name)
return false unless user
cache_key = "user:#{user.id}:#{branch_name}:branch_allows_push"
memoized_results = strong_memoize(:branch_allows_maintainer_push) do
memoized_results = strong_memoize(:branch_allows_collaboration) do
Hash.new do |result, cache_key|
result[cache_key] = fetch_branch_allows_maintainer_push?(user, branch_name)
result[cache_key] = fetch_branch_allows_collaboration?(user, branch_name)
end
end
......@@ -2127,18 +2121,18 @@ class Project < ActiveRecord::Base
raise ex
end
def fetch_branch_allows_maintainer_push?(user, branch_name)
def fetch_branch_allows_collaboration?(user, branch_name)
check_access = -> do
next false if empty_repo?
merge_request = source_of_merge_requests.opened
.where(allow_maintainer_to_push: true)
.where(allow_collaboration: true)
.find_by(source_branch: branch_name)
merge_request&.can_be_merged_by?(user)
end
if RequestStore.active?
RequestStore.fetch("project-#{id}:branch-#{branch_name}:user-#{user.id}:branch_allows_maintainer_push") do
RequestStore.fetch("project-#{id}:branch-#{branch_name}:user-#{user.id}:branch_allows_collaboration") do
check_access.call
end
else
......
......@@ -3,7 +3,7 @@ require 'carrierwave/orm/activerecord'
class ProjectImportData < ActiveRecord::Base
belongs_to :project, inverse_of: :import_data
attr_encrypted :credentials,
key: Gitlab::Application.secrets.db_key_base,
key: Settings.attr_encrypted_db_key_base,
marshal: true,
encode: true,
mode: :per_attribute_iv_and_salt,
......
......@@ -140,10 +140,6 @@ class ProjectWiki
[title, title_array.join("/")]
end
def search_files(query)
repository.search_files_by_content(query, default_branch)
end
def repository
@repository ||= Repository.new(full_path, @project, disk_path: disk_path, is_wiki: true)
end
......
......@@ -5,7 +5,7 @@ class RemoteMirror < ActiveRecord::Base
UNPROTECTED_BACKOFF_DELAY = 5.minutes
attr_encrypted :credentials,
key: Gitlab::Application.secrets.db_key_base,
key: Settings.attr_encrypted_db_key_base,
marshal: true,
encode: true,
mode: :per_attribute_iv_and_salt,
......
......@@ -14,8 +14,8 @@ module Ci
@subject.triggered_by?(@user)
end
condition(:branch_allows_maintainer_push) do
@subject.project.branch_allows_maintainer_push?(@user, @subject.ref)
condition(:branch_allows_collaboration) do
@subject.project.branch_allows_collaboration?(@user, @subject.ref)
end
rule { protected_ref }.policy do
......@@ -25,7 +25,7 @@ module Ci
rule { can?(:admin_build) | (can?(:update_build) & owner_of_job) }.enable :erase_build
rule { can?(:public_access) & branch_allows_maintainer_push }.policy do
rule { can?(:public_access) & branch_allows_collaboration }.policy do
enable :update_build
enable :update_commit_status
end
......
......@@ -4,13 +4,13 @@ module Ci
condition(:protected_ref) { ref_protected?(@user, @subject.project, @subject.tag?, @subject.ref) }
condition(:branch_allows_maintainer_push) do
@subject.project.branch_allows_maintainer_push?(@user, @subject.ref)
condition(:branch_allows_collaboration) do
@subject.project.branch_allows_collaboration?(@user, @subject.ref)
end
rule { protected_ref }.prevent :update_pipeline
rule { can?(:public_access) & branch_allows_maintainer_push }.policy do
rule { can?(:public_access) & branch_allows_collaboration }.policy do
enable :update_pipeline
end
......
......@@ -13,7 +13,7 @@ class MergeRequestWidgetEntity < IssuableEntity
expose :squash
expose :target_branch
expose :target_project_id
expose :allow_maintainer_to_push
expose :allow_collaboration
expose :should_be_rebased?, as: :should_be_rebased
expose :ff_only_enabled do |merge_request|
......
......@@ -90,7 +90,7 @@ module Ci
def builds_for_group_runner
# Workaround for weird Rails bug, that makes `runner.groups.to_sql` to return `runner_id = NULL`
groups = Group.joins(:runner_namespaces).merge(runner.runner_namespaces)
groups = ::Group.joins(:runner_namespaces).merge(runner.runner_namespaces)
hierarchy_groups = Gitlab::GroupHierarchy.new(groups).base_and_descendants
projects = Project.where(namespace_id: hierarchy_groups)
......
......@@ -38,8 +38,8 @@ module MergeRequests
def filter_params(merge_request)
super
unless merge_request.can_allow_maintainer_to_push?(current_user)
params.delete(:allow_maintainer_to_push)
unless merge_request.can_allow_collaboration?(current_user)
params.delete(:allow_collaboration)
end
end
......
class PagesService
attr_reader :data
def initialize(data)
@data = data
end
def execute
return unless Settings.pages.enabled
return unless data[:build_name] == 'pages'
return unless data[:build_status] == 'success'
PagesWorker.perform_async(:deploy, data[:build_id])
end
end
......@@ -11,7 +11,7 @@ module Projects
order: { due_date: :asc, title: :asc }
}
finder_params[:group_ids] = [@project.group.id] if @project.group
finder_params[:group_ids] = @project.group.self_and_ancestors.select(:id) if @project.group
MilestonesFinder.new(finder_params).execute.select([:iid, :title])
end
......
......@@ -17,6 +17,8 @@ module Projects
ensure_wiki_exists if enabling_wiki?
yield if block_given?
if project.update_attributes(params.except(:default_branch))
if project.previous_changes.include?('path')
project.rename_repo
......@@ -36,7 +38,7 @@ module Projects
end
def run_auto_devops_pipeline?
return false if project.repository.gitlab_ci_yml || !project.auto_devops.previous_changes.include?('enabled')
return false if project.repository.gitlab_ci_yml || !project.auto_devops&.previous_changes&.include?('enabled')
project.auto_devops.enabled? || (project.auto_devops.enabled.nil? && Gitlab::CurrentSettings.auto_devops_enabled?)
end
......@@ -53,8 +55,8 @@ module Projects
def changing_default_branch?
new_branch = params[:default_branch]
project.repository.exists? &&
new_branch && new_branch != project.default_branch
new_branch && project.repository.exists? &&
new_branch != project.default_branch
end
def enabling_wiki?
......
......@@ -10,8 +10,6 @@ module ObjectStorage
UnknownStoreError = Class.new(StandardError)
ObjectStorageUnavailable = Class.new(StandardError)
DIRECT_UPLOAD_TIMEOUT = 4.hours
DIRECT_UPLOAD_EXPIRE_OFFSET = 15.minutes
TMP_UPLOAD_PATH = 'tmp/uploads'.freeze
module Store
......@@ -35,7 +33,7 @@ module ObjectStorage
unless current_upload_satisfies?(paths, model)
# the upload we already have isn't right, find the correct one
self.upload = uploads.find_by(model: model, path: paths)
self.upload = model&.retrieve_upload(identifier, paths)
end
super
......@@ -48,7 +46,7 @@ module ObjectStorage
end
def upload=(upload)
return unless upload
return if upload.nil?
self.object_store = upload.store
super
......@@ -157,9 +155,9 @@ module ObjectStorage
model_class.uploader_options.dig(mount_point, :mount_on) || mount_point
end
def workhorse_authorize
def workhorse_authorize(has_length:, maximum_size: nil)
{
RemoteObject: workhorse_remote_upload_options,
RemoteObject: workhorse_remote_upload_options(has_length: has_length, maximum_size: maximum_size),
TempPath: workhorse_local_upload_path
}.compact
end
......@@ -168,23 +166,16 @@ module ObjectStorage
File.join(self.root, TMP_UPLOAD_PATH)
end
def workhorse_remote_upload_options
def workhorse_remote_upload_options(has_length:, maximum_size: nil)
return unless self.object_store_enabled?
return unless self.direct_upload_enabled?
id = [CarrierWave.generate_cache_id, SecureRandom.hex].join('-')
upload_path = File.join(TMP_UPLOAD_PATH, id)
connection = ::Fog::Storage.new(self.object_store_credentials)
expire_at = Time.now + DIRECT_UPLOAD_TIMEOUT + DIRECT_UPLOAD_EXPIRE_OFFSET
options = { 'Content-Type' => 'application/octet-stream' }
direct_upload = ObjectStorage::DirectUpload.new(self.object_store_credentials, remote_store_path, upload_path,
has_length: has_length, maximum_size: maximum_size)
{
ID: id,
Timeout: DIRECT_UPLOAD_TIMEOUT,
GetURL: connection.get_object_url(remote_store_path, upload_path, expire_at),
DeleteURL: connection.delete_object_url(remote_store_path, upload_path, expire_at),
StoreURL: connection.put_object_url(remote_store_path, upload_path, expire_at, options)
}
direct_upload.to_hash.merge(ID: id)
end
end
......
......@@ -23,7 +23,7 @@
.col-sm-10
- checkbox_name = 'application_setting[restricted_visibility_levels][]'
= hidden_field_tag(checkbox_name)
- restricted_level_checkboxes('restricted-visibility-help', checkbox_name).each do |level|
- restricted_level_checkboxes('restricted-visibility-help', checkbox_name, class: 'form-check-input').each do |level|
.form-check
= level
%span.form-text.text-muted#restricted-visibility-help
......@@ -33,7 +33,7 @@
= f.label :import_sources, class: 'col-form-label col-sm-2'
.col-sm-10
= hidden_field_tag 'application_setting[import_sources][]'
- import_sources_checkboxes('import-sources-help').each do |source|
- import_sources_checkboxes('import-sources-help', class: 'form-check-input').each do |source|
.form-check= source
%span.form-text.text-muted#import-sources-help
Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
......
%fieldset
%legend Access
.form-group
= f.label :projects_limit, class: 'col-form-label'
.form-group.row
= f.label :projects_limit, class: 'col-form-label col-sm-2'
.col-sm-10= f.number_field :projects_limit, min: 0, max: Gitlab::Database::MAX_INT_VALUE, class: 'form-control'
.form-group
= f.label :can_create_group, class: 'col-form-label'
.form-group.row
= f.label :can_create_group, class: 'col-form-label col-sm-2'
.col-sm-10= f.check_box :can_create_group
.form-group
= f.label :access_level, class: 'col-form-label'
.form-group.row
= f.label :access_level, class: 'col-form-label col-sm-2'
.col-sm-10
- editing_current_user = (current_user == @user)
= f.radio_button :access_level, :regular, disabled: editing_current_user
= label_tag :regular do
= label_tag :regular, class: 'font-weight-bold' do
Regular
%p.light
Regular users have access to their groups and projects
= f.radio_button :access_level, :admin, disabled: editing_current_user
= label_tag :admin do
= label_tag :admin, class: 'font-weight-bold' do
Admin
%p.light
Administrators have access to all groups, projects and users and can manage all features in this installation
......@@ -28,8 +28,8 @@
%p.light
You cannot remove your own admin rights.
.form-group
= f.label :external, class: 'col-form-label'
.form-group.row
= f.label :external, class: 'col-form-label col-sm-2'
.col-sm-10
= f.check_box :external do
External
......
......@@ -56,7 +56,7 @@
= f.label :linkedin, class: 'col-form-label col-sm-2'
.col-sm-10= f.text_field :linkedin, class: 'form-control'
.form-group.row
= f.label :twitter, class: 'col-form-label'
= f.label :twitter, class: 'col-form-label col-sm-2'
.col-sm-10= f.text_field :twitter, class: 'form-control'
.form-group.row
= f.label :website_url, 'Website', class: 'col-form-label col-sm-2'
......
......@@ -12,11 +12,11 @@
= form_tag personal_access_token_import_gitea_path do
.form-group.row
= label_tag :gitea_host_url, 'Gitea Host URL', class: 'col-form-label col-sm-8'
= label_tag :gitea_host_url, 'Gitea Host URL', class: 'col-form-label col-sm-2'
.col-sm-4
= text_field_tag :gitea_host_url, nil, placeholder: 'https://try.gitea.io', class: 'form-control'
.form-group.row
= label_tag :personal_access_token, 'Personal Access Token', class: 'col-form-label col-sm-8'
= label_tag :personal_access_token, 'Personal Access Token', class: 'col-form-label col-sm-2'
.col-sm-4
= text_field_tag :personal_access_token, nil, class: 'form-control'
.form-actions
......
......@@ -19,7 +19,7 @@
= form_tag personal_access_token_import_github_path, method: :post, class: 'form-inline' do
.form-group
= text_field_tag :personal_access_token, '', class: 'form-control', placeholder: _('Personal Access Token'), size: 40
= text_field_tag :personal_access_token, '', class: 'form-control append-right-8', placeholder: _('Personal Access Token'), size: 40
= submit_tag _('List your GitHub repositories'), class: 'btn btn-success'
- unless github_import_configured?
......
......@@ -14,9 +14,9 @@
%div{ class: "#{container_class} limit-container-width" }
.content{ id: "content-body" }
.panel.panel-default
.panel-heading
.title
.card
.card-header
.card-title
= brand_header_logo
- logo_text = brand_header_logo_type
- if logo_text.present?
......
......@@ -4,7 +4,7 @@
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
.col-lg-4.application-theme
%h4.prepend-top-0
s_('Preferences|Navigation theme')
= s_('Preferences|Navigation theme')
%p Customize the appearance of the application header and navigation sidebar.
.col-lg-8.application-theme
- Gitlab::Themes.each do |theme|
......
......@@ -42,6 +42,10 @@
.project-clone-holder
= render "shared/clone_panel"
- if show_xcode_link?(@project)
.project-action-button.project-xcode.inline
= render "projects/buttons/xcode_link"
- if current_user
- if can?(current_user, :download_code, @project)
= render 'projects/buttons/download', project: @project, ref: @ref
......
.file-content.blob_file.blob-no-preview
.center.render-error.vertical-center
.center.render-error
= link_to blob_raw_path do
%h1.light
= sprite_icon('download')
......
%a.btn.btn-default{ href: xcode_uri_to_repo(@project) }
= _("Open in Xcode")
......@@ -22,7 +22,7 @@
-# Only show it in the first page
- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1')
.prioritized-labels{ class: ('hide' if hide) }
%h5 Prioritized Labels
%h5.prepend-top-10 Prioritized Labels
%ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) }
#js-priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" }
= render 'shared/empty_states/priority_labels'
......
......@@ -29,16 +29,16 @@
.col-lg-9.js-toggle-container
%ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' }
%li{ class: active_when(active_tab == 'blank'), role: 'presentation' }
%a{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%li.nav-item{ role: 'presentation' }
%a.nav-link.active{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.d-none.d-sm-block Blank project
%span.d-block.d-sm-none Blank
%li{ class: active_when(active_tab == 'template'), role: 'presentation' }
%a{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' }
%li.nav-item{ role: 'presentation' }
%a.nav-link{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.d-none.d-sm-block Create from template
%span.d-block.d-sm-none Template
%li{ class: active_when(active_tab == 'import'), role: 'presentation' }
%a{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%li.nav-item{ role: 'presentation' }
%a.nav-link{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.d-none.d-sm-block Import project
%span.d-block.d-sm-none Import
......
- file_name, blob = blob
.blob-result
.file-holder
.js-file-title.file-title
- ref = @search_results.repository_ref
- blob_link = project_blob_path(@project, tree_join(ref, file_name))
= link_to blob_link do
%i.fa.fa-file
%strong
= file_name
- if blob
.file-content.code.term
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link
- project = find_project_for_result_blob(blob)
- file_name, blob = parse_search_result(blob)
- blob_link = project_blob_path(project, tree_join(blob.ref, file_name))
= render partial: 'search/results/blob_data', locals: { blob: blob, project: project, file_name: file_name, blob_link: blob_link }
.blob-result
.file-holder
.js-file-title.file-title
= link_to blob_link do
%i.fa.fa-file
= search_blob_title(project, file_name)
- if blob.data
.file-content.code.term
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline
- wiki_blob = parse_search_result(wiki_blob)
.blob-result
.file-holder
.js-file-title.file-title
= link_to project_wiki_path(@project, wiki_blob.basename) do
%i.fa.fa-file
%strong
= wiki_blob.basename
.file-content.code.term
= render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline
- project = find_project_for_result_blob(wiki_blob)
- file_name, wiki_blob = parse_search_result(wiki_blob)
- wiki_blob_link = project_wiki_path(project, wiki_blob.basename)
= render partial: 'search/results/blob_data', locals: { blob: wiki_blob, project: project, file_name: file_name, blob_link: wiki_blob_link }
- with_label = local_assigns.fetch(:with_label, true)
.form-group.visibility-level-setting
.form-group.row.visibility-level-setting
- if with_label
= f.label :visibility_level, class: 'col-form-label col-sm-2' do
Visibility Level
= link_to icon('question-circle'), help_page_path("public_access/public_access")
%div{ :class => ("col-sm-10" if with_label) }
%div{ :class => (with_label ? "col-sm-10" : "col-sm-12") }
- if can_change_visibility_level
= render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: visibility_level, form_model: form_model)
- else
......
......@@ -12,9 +12,9 @@
= _('Contribution')
.col-sm-10
.form-check
= form.check_box :allow_maintainer_to_push, disabled: !issuable.can_allow_maintainer_to_push?(current_user), class: 'form-check-input'
= form.label :allow_maintainer_to_push, class: 'form-check-label' do
= _('Allow edits from maintainers.')
= link_to 'About this feature', help_page_path('user/project/merge_requests/maintainer_access')
= form.check_box :allow_collaboration, disabled: !issuable.can_allow_collaboration?(current_user), class: 'form-check-input'
= form.label :allow_collaboration, class: 'form-check-label' do
= _('Allow commits from members who can merge to the target branch.')
= link_to 'About this feature', help_page_path('user/project/merge_requests/allow_collaboration')
.form-text.text-muted
= allow_maintainer_push_unavailable_reason(issuable)
= allow_collaboration_unavailable_reason(issuable)
......@@ -15,15 +15,15 @@
- else
= render "shared/issuable/form/metadata_merge_request_assignee", issuable: issuable, form: form, has_due_date: has_due_date
.form-group.row.issue-milestone
= form.label :milestone_id, "Milestone", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-2"}"
.col-10{ class: ("col-md-8" if has_due_date) }
= form.label :milestone_id, "Milestone", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
.col-sm-10{ class: ("col-md-8" if has_due_date) }
.issuable-form-select-holder
= render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, show_started: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
.form-group.row
- has_labels = @labels && @labels.any?
= form.label :label_ids, "Labels", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-2"}"
= form.label :label_ids, "Labels", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
= form.hidden_field :label_ids, multiple: true, value: ''
.col-10{ class: "#{"col-md-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.col-sm-10{ class: "#{"col-md-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.issuable-form-select-holder
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label"
- if has_due_date
......
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
- if @project.branch_allows_maintainer_push?(current_user, selected_branch)
- if @project.branch_allows_collaboration?(current_user, selected_branch)
= commit_in_single_accessible_branch
- else
= commit_in_fork_help
- redirect_params = { redirect: @redirect } if @redirect
.panel-content.rendered-terms
.card-body.rendered-terms
= markdown_field(@term, :terms)
.row-content-block.footer-block.clearfix
.card-footer.footer-block.clearfix
- if can?(current_user, :accept_terms, @term)
.float-right
= button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8' do
......
---
title: Include milestones from parent groups when assigning a milestone to an issue or merge request
merge_request:
author:
type: changed
---
title: Rephrasing Merge Request's 'allow edits from maintainer' functionality
merge_request: 19061
author:
type: deprecated
---
title: Add Open in Xcode link for xcode repositories
merge_request:
author:
type: added
---
title: Check for nil AutoDevOps when saving project CI/CD settings.
merge_request: 19190
author:
type: fixed
---
title: Update email_spec to 2.2.0
merge_request: 19164
author: Takuya Noguchi
type: other
---
title: Update selenium-webdriver to 3.12.0
merge_request: 19351
author: Takuya Noguchi
type: other
---
title: Add background migrations for archiving legacy job traces
merge_request: 19194
author:
type: performance
---
title: Include username in output when testing SSH to GitLab
merge_request: 19358
author:
type: other
---
title: Fix an N+1 when loading user avatars
merge_request:
author:
type: performance
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment