Commit 7e5ac711 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'master' into '36133-project-overview-page-doesn-t-load-commit-metadata-for-repo-view'

# Conflicts:
#   app/assets/javascripts/dispatcher.js
parents 3d08e472 2925850c
......@@ -514,8 +514,11 @@ codeclimate:
services:
- docker:dind
script:
- cp .rubocop.yml .rubocop.yml.bak
- grep -v "rubocop-gitlab-security" .rubocop.yml.bak > .rubocop.yml
- docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate analyze -f json > raw_codeclimate.json
- cat raw_codeclimate.json | docker run -i stedolan/jq -c 'map({check_name,fingerprint,location})' > codeclimate.json
- mv .rubocop.yml.bak .rubocop.yml
artifacts:
paths: [codeclimate.json]
......
require:
- rubocop-rspec
- rubocop-gitlab-security
- ./rubocop/rubocop
inherit_from: .rubocop_todo.yml
......@@ -206,6 +207,13 @@ Layout/SpaceAroundKeyword:
Layout/SpaceAroundOperators:
Enabled: true
# Checks that block braces have or don't have a space before the opening
# brace depending on configuration.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: space, no_space
Layout/SpaceBeforeBlockBraces:
Enabled: true
# No spaces before commas.
Layout/SpaceBeforeComma:
Enabled: true
......@@ -1156,3 +1164,35 @@ RSpec/SubjectStub:
# Prefer using verifying doubles over normal doubles.
RSpec/VerifiedDoubles:
Enabled: false
# GitlabSecurity ##############################################################
GitlabSecurity/DeepMunge:
Enabled: true
Exclude:
- 'spec/**/*'
- 'lib/**/*.rake'
GitlabSecurity/PublicSend:
Enabled: true
Exclude:
- 'spec/**/*'
- 'lib/**/*.rake'
GitlabSecurity/RedirectToParamsUpdate:
Enabled: true
Exclude:
- 'spec/**/*'
- 'lib/**/*.rake'
GitlabSecurity/SqlInjection:
Enabled: true
Exclude:
- 'spec/**/*'
- 'lib/**/*.rake'
GitlabSecurity/SystemCommandInjection:
Enabled: true
Exclude:
- 'spec/**/*'
- 'lib/**/*.rake'
......@@ -26,13 +26,6 @@ Layout/IndentArray:
Layout/IndentHash:
Enabled: false
# Offense count: 174
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: space, no_space
Layout/SpaceBeforeBlockBraces:
Enabled: false
# Offense count: 8
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment.
......
......@@ -341,6 +341,7 @@ group :development, :test do
gem 'rubocop', '~> 0.49.1', require: false
gem 'rubocop-rspec', '~> 1.15.1', require: false
gem 'rubocop-gitlab-security', '~> 0.0.6', require: false
gem 'scss_lint', '~> 0.54.0', require: false
gem 'haml_lint', '~> 0.26.0', require: false
gem 'simplecov', '~> 0.14.0', require: false
......@@ -354,7 +355,7 @@ group :development, :test do
gem 'activerecord_sane_schema_dumper', '0.2'
gem 'stackprof', '~> 0.2.10'
gem 'stackprof', '~> 0.2.10', require: false
end
group :test do
......
......@@ -548,7 +548,7 @@ GEM
rubypants (~> 0.2)
orm_adapter (0.5.0)
os (0.9.6)
parallel (1.11.2)
parallel (1.12.0)
paranoia (2.3.1)
activerecord (>= 4.0, < 5.2)
parser (2.4.0.0)
......@@ -741,8 +741,9 @@ GEM
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.0.6)
rubocop (>= 0.47.1)
rubocop-rspec (1.15.1)
rubocop (>= 0.42.0)
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-prof (0.16.2)
......@@ -1088,6 +1089,7 @@ DEPENDENCIES
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
rubocop (~> 0.49.1)
rubocop-gitlab-security (~> 0.0.6)
rubocop-rspec (~> 1.15.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2)
......
......@@ -119,6 +119,12 @@ only be left until after the freeze if:
are aware of it.
* It is in the correct milestone, with the ~Deliverable label.
If a merge request is not ready, but the developers and Product Manager
responsible for the feature think it is essential that it is in the release,
they can [ask for an exception](#asking-for-an-exception) in advance. This is
preferable to merging something that we are not confident in, but should still
be a rare case: most features can be allowed to slip a release.
All Community Edition merge requests from GitLab team members merged on the
freeze date (the 7th) should have a corresponding Enterprise Edition merge
request, even if there are no conflicts. This is to reduce the size of the
......@@ -128,11 +134,26 @@ information, see
### After the 7th
Once the stable branch is frozen, only fixes for [regressions](#regressions)
and security issues will be cherry-picked into the stable branch.
Any merge requests cherry-picked into the stable branch for a previous release will also be picked into the latest stable branch.
These fixes will be shipped in the next RC for that release if it is before the 22nd.
If the fixes are are completed on or after the 22nd, they will be shipped in a patch for that release.
Once the stable branch is frozen, the only MRs that can be cherry-picked into
the stable branch are:
* Fixes for [regressions](#regressions)
* Fixes for security issues
* New or updated translations (as long as they do not touch application code)
Any merge requests cherry-picked into the stable branch for a previous release
will also be picked into the latest stable branch. These fixes will be shipped
in the next RC for that release if it is before the 22nd. If the fixes are are
completed on or after the 22nd, they will be shipped in a patch for that
release.
During the feature freeze all merge requests that are meant to go into the upcoming
release should have the correct milestone assigned _and_ have the label
~"Pick into Stable" set, so that release managers can find and pick them.
Merge requests without a milestone and this label will
not be merged into any stable branches.
### Asking for an exception
If you think a merge request should go into an RC or patch even though it does not meet these requirements,
you can ask for an exception to be made. Exceptions require sign-off from 3 people besides the developer:
......@@ -152,11 +173,7 @@ When in doubt, we err on the side of _not_ cherry-picking.
For example, it is likely that an exception will be made for a trivial 1-5 line performance improvement
(e.g. adding a database index or adding `includes` to a query), but not for a new feature, no matter how relatively small or thoroughly tested.
During the feature freeze all merge requests that are meant to go into the upcoming
release should have the correct milestone assigned _and_ have the label
~"Pick into Stable" set, so that release managers can find and pick them.
Merge requests without a milestone and this label will
not be merged into any stable branches.
All MRs which have had exceptions granted must be merged by the 15th.
### Regressions
......
/* eslint-disable func-names, object-shorthand, prefer-arrow-callback */
/* global Dropzone */
import '../lib/utils/url_utility';
import { HIDDEN_CLASS } from '../lib/utils/constants';
function toggleLoading($el, $icon, loading) {
if (loading) {
$el.disable();
$icon.removeClass(HIDDEN_CLASS);
} else {
$el.enable();
$icon.addClass(HIDDEN_CLASS);
}
}
export default class BlobFileDropzone {
constructor(form, method) {
const formDropzone = form.find('.dropzone');
const submitButton = form.find('#submit-all');
const submitButtonLoadingIcon = submitButton.find('.js-loading-icon');
const dropzoneMessage = form.find('.dz-message');
Dropzone.autoDiscover = false;
const dropzone = formDropzone.dropzone({
......@@ -26,12 +41,20 @@ export default class BlobFileDropzone {
},
init: function () {
this.on('addedfile', function () {
toggleLoading(submitButton, submitButtonLoadingIcon, false);
dropzoneMessage.addClass(HIDDEN_CLASS);
$('.dropzone-alerts').html('').hide();
});
this.on('removedfile', function () {
toggleLoading(submitButton, submitButtonLoadingIcon, false);
dropzoneMessage.removeClass(HIDDEN_CLASS);
});
this.on('success', function (header, response) {
window.location.href = response.filePath;
$('#modal-upload-blob').modal('hide');
window.gl.utils.visitUrl(response.filePath);
});
this.on('maxfilesexceeded', function (file) {
dropzoneMessage.addClass(HIDDEN_CLASS);
this.removeFile(file);
});
this.on('sending', function (file, xhr, formData) {
......@@ -48,14 +71,15 @@ export default class BlobFileDropzone {
},
});
const submitButton = form.find('#submit-all')[0];
submitButton.addEventListener('click', function (e) {
submitButton.on('click', (e) => {
e.preventDefault();
e.stopPropagation();
if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
// eslint-disable-next-line no-alert
alert('Please select a file');
return false;
}
toggleLoading(submitButton, submitButtonLoadingIcon, true);
dropzone[0].dropzone.processQueue();
return false;
});
......
......@@ -343,6 +343,7 @@ import UserFeatureHelper from './helpers/user_feature_helper';
if ($('#tree-slider').length) new TreeView();
if ($('.blob-viewer').length) new BlobViewer();
if ($('.project-show-activity').length) new gl.Activities();
$('#tree-slider').waitForImages(function() {
gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
});
......
/* eslint-disable wrap-iife, func-names, space-before-function-paren, comma-dangle, prefer-template, consistent-return, class-methods-use-this, arrow-body-style, no-unused-vars, no-underscore-dangle, no-new, max-len, no-sequences, no-unused-expressions, no-param-reassign */
/* global dateFormat */
/* global Pikaday */
import Pikaday from 'pikaday';
import DateFix from './lib/utils/datefix';
class DueDateSelect {
......
/* global bp */
import Cookies from 'js-cookie';
import './breakpoints';
export const canShowSubItems = () => bp.getBreakpointSize() === 'md' || bp.getBreakpointSize() === 'lg';
export const canShowActiveSubItems = (el) => {
const isHiddenByMedia = bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md';
if (el.classList.contains('active') && !isHiddenByMedia) {
return Cookies.get('sidebar_collapsed') === 'true';
}
return true;
};
export const canShowSubItems = () => bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md' || bp.getBreakpointSize() === 'lg';
export const calculateTop = (boundingRect, outerHeight) => {
const windowHeight = window.innerHeight;
......@@ -14,9 +24,10 @@ export const calculateTop = (boundingRect, outerHeight) => {
export const showSubLevelItems = (el) => {
const subItems = el.querySelector('.sidebar-sub-level-items');
if (!subItems || !canShowSubItems()) return;
if (!subItems || !canShowSubItems() || !canShowActiveSubItems(el)) return;
subItems.style.display = 'block';
el.classList.add('is-showing-fly-out');
el.classList.add('is-over');
const boundingRect = el.getBoundingClientRect();
......@@ -34,15 +45,16 @@ export const showSubLevelItems = (el) => {
export const hideSubLevelItems = (el) => {
const subItems = el.querySelector('.sidebar-sub-level-items');
if (!subItems || !canShowSubItems()) return;
if (!subItems || !canShowSubItems() || !canShowActiveSubItems(el)) return;
el.classList.remove('is-showing-fly-out');
el.classList.remove('is-over');
subItems.style.display = 'none';
subItems.classList.remove('is-above');
};
export default () => {
const items = [...document.querySelectorAll('.sidebar-top-level-items > li:not(.active)')]
const items = [...document.querySelectorAll('.sidebar-top-level-items > li')]
.filter(el => el.querySelector('.sidebar-sub-level-items'));
items.forEach((el) => {
......
import Chart from 'vendor/Chart';
import _ from 'underscore';
document.addEventListener('DOMContentLoaded', () => {
const projectChartData = JSON.parse(document.getElementById('projectChartData').innerHTML);
......@@ -27,28 +28,25 @@ document.addEventListener('DOMContentLoaded', () => {
return generateChart();
};
const chartData = (keys, values) => {
const data = {
labels: keys,
const chartData = data => ({
labels: Object.keys(data),
datasets: [{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: values,
data: _.values(data),
}],
};
return data;
};
});
const hourData = chartData(projectChartData.hour.keys, projectChartData.hour.values);
const hourData = chartData(projectChartData.hour);
responsiveChart($('#hour-chart'), hourData);
const dayData = chartData(projectChartData.weekDays.keys, projectChartData.weekDays.values);
const dayData = chartData(projectChartData.weekDays);
responsiveChart($('#weekday-chart'), dayData);
const monthData = chartData(projectChartData.month.keys, projectChartData.month.values);
const monthData = chartData(projectChartData.month);
responsiveChart($('#month-chart'), monthData);
const data = projectChartData.languages;
......
......@@ -2,8 +2,8 @@
/* global GitLab */
/* global Autosave */
/* global dateFormat */
/* global Pikaday */
import Pikaday from 'pikaday';
import UsersSelect from './users_select';
import GfmAutoComplete from './gfm_auto_complete';
import ZenMode from './zen_mode';
......
/* eslint-disable import/prefer-default-export */
export const BYTES_IN_KIB = 1024;
export const HIDDEN_CLASS = 'hidden';
......@@ -7,7 +7,6 @@
import jQuery from 'jquery';
import _ from 'underscore';
import Cookies from 'js-cookie';
import Pikaday from 'pikaday';
import Dropzone from 'dropzone';
import Sortable from 'vendor/Sortable';
......@@ -20,7 +19,6 @@ import 'vendor/fuzzaldrin-plus';
window.jQuery = jQuery;
window.$ = jQuery;
window._ = _;
window.Pikaday = Pikaday;
window.Dropzone = Dropzone;
window.Sortable = Sortable;
......
/* global Pikaday */
/* global dateFormat */
import Pikaday from 'pikaday';
(() => {
// Add datepickers to all `js-access-expiration-date` elements. If those elements are
// children of an element with the `clearable-input` class, and have a sibling
......
......@@ -372,6 +372,10 @@ table {
background: $gl-success !important;
}
.dz-message {
margin: 0;
}
.space-right {
margin-right: 10px;
}
......
......@@ -26,7 +26,7 @@ header {
&.navbar-gitlab {
padding: 0 16px;
z-index: 2000;
z-index: 1000;
margin-bottom: 0;
min-height: $header-height;
background-color: $gray-light;
......
......@@ -162,3 +162,5 @@ $pre-color: $gl-text-color !default;
$pre-border-color: $border-color;
$table-bg-accent: $gray-light;
$zindex-popover: 900;
......@@ -104,11 +104,14 @@ $new-sidebar-collapsed-width: 50px;
&.sidebar-icons-only {
width: $new-sidebar-collapsed-width;
.nav-item-name,
.badge,
.project-title {
display: none;
}
.nav-item-name {
opacity: 0;
}
}
&.nav-sidebar-expanded {
......@@ -182,7 +185,7 @@ $new-sidebar-collapsed-width: 50px;
> li {
a {
padding: 8px 16px 8px 50px;
padding: 8px 16px 8px 40px;
&:hover,
&:focus {
......@@ -215,12 +218,15 @@ $new-sidebar-collapsed-width: 50px;
&:hover {
color: $gl-text-color;
svg {
fill: $gl-text-color;
}
}
}
&:not(.active) {
&.is-showing-fly-out {
> a {
margin-left: 1px;
margin-right: 2px;
}
......@@ -229,7 +235,7 @@ $new-sidebar-collapsed-width: 50px;
position: fixed;
top: 0;
left: $new-sidebar-width;
width: 150px;
min-width: 150px;
margin-top: -1px;
padding: 8px 1px;
background-color: $white-light;
......@@ -271,6 +277,14 @@ $new-sidebar-collapsed-width: 50px;
}
}
> .active {
box-shadow: none;
> a {
background-color: transparent;
}
}
a {
padding: 8px 16px;
color: $gl-text-color;
......@@ -294,6 +308,7 @@ $new-sidebar-collapsed-width: 50px;
> a {
margin-left: 4px;
padding-left: 12px;
}
.badge {
......@@ -354,7 +369,7 @@ $new-sidebar-collapsed-width: 50px;
.sidebar-icons-only {
.context-header {
height: 60px;
height: 61px;
a {
padding: 10px 4px;
......
......@@ -35,7 +35,7 @@
.commit-box,
.info-well,
.commit-ci-menu,
.files-changed,
.files-changed-inner,
.limited-header-width,
.limited-width-notes {
@extend .fixed-width-container;
......
......@@ -216,6 +216,9 @@
}
.blob-upload-dropzone-previews {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
border: 2px;
border-style: dashed;
......
......@@ -117,7 +117,7 @@ class ApplicationController < ActionController::Base
Raven.capture_exception(exception) if sentry_enabled?
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
application_trace.map!{ |t| " #{t}\n" }
application_trace.map! { |t| " #{t}\n" }
logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}"
end
......
......@@ -68,15 +68,15 @@ class Import::GithubController < Import::BaseController
end
def new_import_url
public_send("new_import_#{provider}_url")
public_send("new_import_#{provider}_url") # rubocop:disable GitlabSecurity/PublicSend
end
def status_import_url
public_send("status_import_#{provider}_url")
public_send("status_import_#{provider}_url") # rubocop:disable GitlabSecurity/PublicSend
end
def callback_import_url
public_send("callback_import_#{provider}_url")
public_send("callback_import_#{provider}_url") # rubocop:disable GitlabSecurity/PublicSend
end
def provider_unauthorized
......
......@@ -15,7 +15,7 @@ class Import::GitlabController < Import::BaseController
@already_added_projects = current_user.created_projects.where(import_type: "gitlab")
already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos = @repos.to_a.reject{ |repo| already_added_projects_names.include? repo["path_with_namespace"] }
@repos = @repos.to_a.reject { |repo| already_added_projects_names.include? repo["path_with_namespace"] }
end
def jobs
......
......@@ -3,7 +3,7 @@ module GraphHelper
refs = ""
# Commit::ref_names already strips the refs/XXX from important refs (e.g. refs/heads/XXX)
# so anything leftover is internally used by GitLab
commit_refs = commit.ref_names(repo).reject{ |name| name.starts_with?('refs/') }
commit_refs = commit.ref_names(repo).reject { |name| name.starts_with?('refs/') }
refs << commit_refs.join(' ')
# append note count
......
......@@ -151,7 +151,7 @@ module IssuablesHelper
end
def issuable_labels_tooltip(labels, limit: 5)
first, last = labels.partition.with_index{ |_, i| i < limit }
first, last = labels.partition.with_index { |_, i| i < limit }
label_names = first.collect(&:name)
label_names << "and #{last.size} more" unless last.empty?
......@@ -234,7 +234,7 @@ module IssuablesHelper
end
def issuables_count_for_state(issuable_type, state, finder: nil)
finder ||= public_send("#{issuable_type}_finder")
finder ||= public_send("#{issuable_type}_finder") # rubocop:disable GitlabSecurity/PublicSend
cache_key = finder.state_counter_cache_key
@counts ||= {}
......@@ -329,7 +329,7 @@ module IssuablesHelper
end
def selected_template(issuable)
params[:issuable_template] if issuable_templates(issuable).any?{ |template| template[:name] == params[:issuable_template] }
params[:issuable_template] if issuable_templates(issuable).any? { |template| template[:name] == params[:issuable_template] }
end
def issuable_todo_button_data(issuable, todo, is_collapsed)
......
......@@ -43,11 +43,11 @@ module LabelsHelper
def label_filter_path(subject, label, type: :issue)
case subject
when Group
send("#{type.to_s.pluralize}_group_path",
send("#{type.to_s.pluralize}_group_path", # rubocop:disable GitlabSecurity/PublicSend
subject,
label_name: [label.name])
when Project
send("namespace_project_#{type.to_s.pluralize}_path",
send("namespace_project_#{type.to_s.pluralize}_path", # rubocop:disable GitlabSecurity/PublicSend
subject.namespace,
subject,
label_name: [label.name])
......
......@@ -55,7 +55,8 @@ class Commit
end
def from_hash(hash, project)
new(Gitlab::Git::Commit.new(hash), project)
raw_commit = Gitlab::Git::Commit.new(project.repository.raw, hash)
new(raw_commit, project)
end
def valid_hash?(key)
......@@ -320,21 +321,11 @@ class Commit
end
def raw_diffs(*args)
if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
Gitlab::GitalyClient::CommitService.new(project.repository).diff_from_parent(self, *args)
else
raw.diffs(*args)
end
end
def raw_deltas
@deltas ||= Gitlab::GitalyClient.migrate(:commit_deltas) do |is_enabled|
if is_enabled
Gitlab::GitalyClient::CommitService.new(project.repository).commit_deltas(self)
else
raw.deltas
end
end
@deltas ||= raw.deltas
end
def diffs(diff_options = nil)
......
......@@ -58,7 +58,7 @@ module Spammable
options.fetch(:spam_title, false)
end
public_send(attr.first) if attr && respond_to?(attr.first.to_sym)
public_send(attr.first) if attr && respond_to?(attr.first.to_sym) # rubocop:disable GitlabSecurity/PublicSend
end
def spam_description
......@@ -66,12 +66,12 @@ module Spammable
options.fetch(:spam_description, false)
end
public_send(attr.first) if attr && respond_to?(attr.first.to_sym)
public_send(attr.first) if attr && respond_to?(attr.first.to_sym) # rubocop:disable GitlabSecurity/PublicSend
end
def spammable_text
result = self.class.spammable_attrs.map do |attr|
public_send(attr.first)
public_send(attr.first) # rubocop:disable GitlabSecurity/PublicSend
end
result.reject(&:blank?).join("\n")
......
......@@ -44,7 +44,8 @@ module TokenAuthenticatable
end
define_method("ensure_#{token_field}!") do
send("reset_#{token_field}!") if read_attribute(token_field).blank?
send("reset_#{token_field}!") if read_attribute(token_field).blank? # rubocop:disable GitlabSecurity/PublicSend
read_attribute(token_field)
end
......
......@@ -162,7 +162,7 @@ class MergeRequest < ActiveRecord::Base
target = unscoped.where(target_project_id: relation).select(:id)
union = Gitlab::SQL::Union.new([source, target])
where("merge_requests.id IN (#{union.to_sql})")
where("merge_requests.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
end
WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
......
......@@ -282,9 +282,7 @@ class MergeRequestDiff < ActiveRecord::Base
def load_commits
commits = st_commits.presence || merge_request_diff_commits
commits.map do |commit|
Commit.new(Gitlab::Git::Commit.new(commit.to_hash), merge_request.source_project)
end
commits.map { |commit| Commit.from_hash(commit.to_hash, project) }
end
def save_diffs
......
......@@ -26,7 +26,7 @@ class MergeRequestDiffCommit < ActiveRecord::Base
def to_hash
Gitlab::Git::Commit::SERIALIZE_KEYS.each_with_object({}) do |key, hash|
hash[key] = public_send(key)
hash[key] = public_send(key) # rubocop:disable GitlabSecurity/PublicSend
end
end
......
......@@ -206,7 +206,7 @@ module Network
# Visit branching chains
leaves.each do |l|
parents = l.parents(@map).select{|p| p.space.zero?}
parents = l.parents(@map).select {|p| p.space.zero?}
parents.each do |p|
place_chain(p, l.time)
end
......
......@@ -77,20 +77,20 @@ class Note < ActiveRecord::Base
# Scopes
scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) }
scope :system, ->{ where(system: true) }
scope :user, ->{ where(system: false) }
scope :common, ->{ where(noteable_type: ["", nil]) }
scope :fresh, ->{ order(created_at: :asc, id: :asc) }
scope :updated_after, ->(time){ where('updated_at > ?', time) }
scope :inc_author_project, ->{ includes(:project, :author) }
scope :inc_author, ->{ includes(:author) }
scope :system, -> { where(system: true) }
scope :user, -> { where(system: false) }
scope :common, -> { where(noteable_type: ["", nil]) }
scope :fresh, -> { order(created_at: :asc, id: :asc) }
scope :updated_after, ->(time) { where('updated_at > ?', time) }
scope :inc_author_project, -> { includes(:project, :author) }
scope :inc_author, -> { includes(:author) }
scope :inc_relations_for_view, -> do
includes(:project, :author, :updated_by, :resolved_by, :award_emoji, :system_note_metadata)
end
scope :diff_notes, ->{ where(type: %w(LegacyDiffNote DiffNote)) }
scope :new_diff_notes, ->{ where(type: 'DiffNote') }
scope :non_diff_notes, ->{ where(type: ['Note', 'DiscussionNote', nil]) }
scope :diff_notes, -> { where(type: %w(LegacyDiffNote DiffNote)) }
scope :new_diff_notes, -> { where(type: 'DiffNote') }
scope :non_diff_notes, -> { where(type: ['Note', 'DiscussionNote', nil]) }
scope :with_associations, -> do
# FYI noteable cannot be loaded for LegacyDiffNote for commits
......
......@@ -66,6 +66,6 @@ class NotificationSetting < ActiveRecord::Base
alias_method :failed_pipeline?, :failed_pipeline
def event_enabled?(event)
respond_to?(event) && !!public_send(event)
respond_to?(event) && !!public_send(event) # rubocop:disable GitlabSecurity/PublicSend
end
end
......@@ -415,7 +415,7 @@ class Project < ActiveRecord::Base
union = Gitlab::SQL::Union.new([projects, namespaces])
where("projects.id IN (#{union.to_sql})")
where("projects.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
end
def search_by_title(query)
......@@ -825,7 +825,7 @@ class Project < ActiveRecord::Base
if template.nil?
# If no template, we should create an instance. Ex `build_gitlab_ci_service`
public_send("build_#{service_name}_service")
public_send("build_#{service_name}_service") # rubocop:disable GitlabSecurity/PublicSend
else
Service.build_from_template(id, template)
end
......@@ -1046,6 +1046,7 @@ class Project < ActiveRecord::Base
end
def change_head(branch)
if repository.branch_exists?(branch)
repository.before_change_head
repository.rugged.references.create('HEAD',
"refs/heads/#{branch}",
......@@ -1053,6 +1054,10 @@ class Project < ActiveRecord::Base
repository.copy_gitattributes(branch)
repository.after_change_head
reload_default_branch
else
errors.add(:base, "Could not change HEAD: branch '#{branch}' does not exist")
false
end
end
def forked_from?(project)
......@@ -1326,7 +1331,7 @@ class Project < ActiveRecord::Base
end
def append_or_update_attribute(name, value)
old_values = public_send(name.to_s)
old_values = public_send(name.to_s) # rubocop:disable GitlabSecurity/PublicSend
if Project.reflect_on_association(name).try(:macro) == :has_many && old_values.any?
update_attribute(name, old_values + value)
......
......@@ -55,7 +55,7 @@ class ProjectFeature < ActiveRecord::Base
end
def access_level(feature)
public_send(ProjectFeature.access_level_attribute(feature))
public_send(ProjectFeature.access_level_attribute(feature)) # rubocop:disable GitlabSecurity/PublicSend
end
def builds_enabled?
......@@ -80,7 +80,7 @@ class ProjectFeature < ActiveRecord::Base
# which cannot be higher than repository access level
def repository_children_level
validator = lambda do |field|
level = public_send(field) || ProjectFeature::ENABLED
level = public_send(field) || ProjectFeature::ENABLED # rubocop:disable GitlabSecurity/PublicSend
not_allowed = level > repository_access_level
self.errors.add(field, "cannot have higher visibility level than repository access level") if not_allowed
end
......
......@@ -14,7 +14,7 @@ class ProjectStatistics < ActiveRecord::Base
def refresh!(only: nil)
STATISTICS_COLUMNS.each do |column, generator|
if only.blank? || only.include?(column)
public_send("update_#{column}")
public_send("update_#{column}") # rubocop:disable GitlabSecurity/PublicSend
end
end
......
......@@ -64,6 +64,8 @@ class Repository
@raw_repository ||= initialize_raw_repository
end
alias_method :raw, :raw_repository
# Return absolute path to repository
def path_to_repo
@path_to_repo ||= File.expand_path(
......@@ -298,7 +300,7 @@ class Repository
expire_method_caches(to_refresh)
to_refresh.each { |method| send(method) }
to_refresh.each { |method| send(method) } # rubocop:disable GitlabSecurity/PublicSend
end
def expire_branch_cache(branch_name = nil)
......@@ -763,7 +765,7 @@ class Repository
index = Gitlab::Git::Index.new(raw_repository)
if start_commit
index.read_tree(start_commit.raw_commit.tree)
index.read_tree(start_commit.rugged_commit.tree)
parents = [start_commit.sha]
else
parents = []
......
......@@ -148,6 +148,8 @@ class User < ActiveRecord::Base
uniqueness: { case_sensitive: false }
validate :namespace_uniq, if: :username_changed?
validate :namespace_move_dir_allowed, if: :username_changed?
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validate :unique_email, if: :email_changed?
validate :owns_notification_email, if: :notification_email_changed?
......@@ -487,6 +489,12 @@ class User < ActiveRecord::Base
end
end
def namespace_move_dir_allowed
if namespace&.any_project_has_container_registry_tags?
errors.add(:username, 'cannot be changed if a personal project has container registry tags.')
end
end
def avatar_type
unless avatar.image?
errors.add :avatar, "only images allowed"
......@@ -528,7 +536,7 @@ class User < ActiveRecord::Base
union = Gitlab::SQL::Union
.new([groups.select(:id), authorized_projects.select(:namespace_id)])
Group.where("namespaces.id IN (#{union.to_sql})")
Group.where("namespaces.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
end
# Returns a relation of groups the user has access to, including their parent
......@@ -719,8 +727,8 @@ class User < ActiveRecord::Base
def sanitize_attrs
%w[username skype linkedin twitter].each do |attr|
value = public_send(attr)
public_send("#{attr}=", Sanitize.clean(value)) if value.present?
value = public_send(attr) # rubocop:disable GitlabSecurity/PublicSend
public_send("#{attr}=", Sanitize.clean(value)) if value.present? # rubocop:disable GitlabSecurity/PublicSend
end
end
......@@ -779,7 +787,7 @@ class User < ActiveRecord::Base
def with_defaults
User.defaults.each do |k, v|
public_send("#{k}=", v)
public_send("#{k}=", v) # rubocop:disable GitlabSecurity/PublicSend
end
self
......@@ -825,7 +833,7 @@ class User < ActiveRecord::Base
{
name: name,
username: username,
avatar_url: avatar_url
avatar_url: avatar_url(only_path: false)
}
end
......@@ -919,7 +927,7 @@ class User < ActiveRecord::Base
def ci_authorized_runners
@ci_authorized_runners ||= begin
runner_ids = Ci::RunnerProject
.where("ci_runner_projects.project_id IN (#{ci_projects_union.to_sql})")
.where("ci_runner_projects.project_id IN (#{ci_projects_union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
.select(:runner_id)
Ci::Runner.specific.where(id: runner_ids)
end
......
......@@ -35,6 +35,6 @@ class AnalyticsBuildEntity < Grape::Entity
private
def url_to(route, build, id = nil)
public_send("#{route}_url", build.project.namespace, build.project, id || build)
public_send("#{route}_url", build.project.namespace, build.project, id || build) # rubocop:disable GitlabSecurity/PublicSend
end
end
......@@ -24,6 +24,6 @@ class AnalyticsIssueEntity < Grape::Entity
private
def url_to(route, id)
public_send("#{route}_url", request.project.namespace, request.project, id)
public_send("#{route}_url", request.project.namespace, request.project, id) # rubocop:disable GitlabSecurity/PublicSend
end
end
......@@ -46,6 +46,6 @@ class JobEntity < Grape::Entity
end
def path_to(route, build)
send("#{route}_path", build.project.namespace, build.project, build)
send("#{route}_path", build.project.namespace, build.project, build) # rubocop:disable GitlabSecurity/PublicSend
end
end
......@@ -16,6 +16,7 @@ module Issues
spam_check(issue, current_user)
issue.move_to_end
# current_user (defined in BaseService) is not available within run_after_commit block
user = current_user
issue.run_after_commit do
NewIssueWorker.perform_async(issue.id, user.id)
......
......@@ -37,7 +37,7 @@ module Labels
union = Gitlab::SQL::Union.new(label_ids)
Label.where("labels.id IN (#{union.to_sql})").reorder(nil).uniq
Label.where("labels.id IN (#{union.to_sql})").reorder(nil).uniq # rubocop:disable GitlabSecurity/SqlInjection
end
def group_labels_applied_to_issues
......
......@@ -17,6 +17,7 @@ module MergeRequests
end
def before_create(merge_request)
# current_user (defined in BaseService) is not available within run_after_commit block
user = current_user
merge_request.run_after_commit do
NewMergeRequestWorker.perform_async(merge_request.id, user.id)
......
......@@ -10,7 +10,7 @@ module Projects
end
if changing_default_branch?
project.change_head(params[:default_branch])
return error("Could not set the default branch") unless project.change_head(params[:default_branch])
end
if project.update_attributes(params.except(:default_branch))
......
......@@ -74,7 +74,8 @@
= link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username }
%li
= link_to "Settings", profile_path
= render 'shared/user_dropdown_experimental_features'
%li
= link_to "Turn on new navigation", profile_preferences_path(anchor: "new-navigation")
%li.divider
%li
= link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link"
......
......@@ -68,7 +68,8 @@
= link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username }
%li
= link_to "Settings", profile_path
= render 'shared/user_dropdown_experimental_features'
%li
= link_to "Turn off new navigation", profile_preferences_path(anchor: "new-navigation")
%li.divider
%li
= link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link"
......
......@@ -18,8 +18,6 @@
= scheme.name
.col-sm-12
%hr
%h3#experimental-features Experimental features
%hr
.col-lg-4.profile-settings-sidebar#new-navigation
%h4.prepend-top-0
New Navigation
......@@ -42,28 +40,6 @@
New
.col-sm-12
%hr
.col-lg-4.profile-settings-sidebar#new-repository
%h4.prepend-top-0
New Repository
%p
This setting allows you to turn on or off the new upcoming repository concept.
.col-lg-8.syntax-theme
.nav-wip
%p
The new repository is currently a work-in-progress concept and only usable on wide-screens. There are a number of improvements that we are working on in order to further refine the repository view.
%p
%a{ href: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/31890', target: 'blank' } Learn more
about the improvements that are coming soon!
= label_tag do
.preview= image_tag "old_repo.png"
%input.js-experiment-feature-toggle{ type: "radio", value: "false", name: "new_repo", checked: !show_new_repo? }
Old
= label_tag do
.preview= image_tag "new_repo.png"
%input.js-experiment-feature-toggle{ type: "radio", value: "true", name: "new_repo", checked: show_new_repo? }
New
.col-sm-12
%hr
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
Behavior
......
......@@ -19,7 +19,9 @@
= render 'shared/new_commit_form', placeholder: placeholder
.form-actions
= button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all'
= button_tag class: 'btn btn-create btn-upload-file', id: 'submit-all', type: 'button' do
= icon('spin spinner', class: 'js-loading-icon hidden' )
= button_title
= link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project)
......
......@@ -4,6 +4,7 @@
- diff_files = diffs.diff_files
.content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed
.files-changed-inner
.inline-parallel-buttons
- if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? }
= link_to 'Expand all', url_for(params.merge(expanded: 1, format: nil)), class: 'btn btn-default'
......
......@@ -78,8 +78,8 @@
%script#projectChartData{ type: "application/json" }
- projectChartData = {};
- projectChartData['hour'] = { 'keys' => @commits_per_time.keys, 'values' => @commits_per_time.values }
- projectChartData['weekDays'] = { 'keys' => @commits_per_week_days.keys, 'values' => @commits_per_week_days.values }
- projectChartData['month'] = { 'keys' => @commits_per_month.keys, 'values' => @commits_per_month.values }
- projectChartData['hour'] = @commits_per_time
- projectChartData['weekDays'] = @commits_per_week_days
- projectChartData['month'] = @commits_per_month
- projectChartData['languages'] = @languages
= projectChartData.to_json.html_safe
%li= link_to 'Experimental features', profile_preferences_path(anchor: 'experimental-features')
module NewIssuable
attr_reader :issuable, :user
def ensure_objects_found(issuable_id, user_id)
@issuable = issuable_class.find_by(id: issuable_id)
unless @issuable
log_error(issuable_class, issuable_id)
return false
def objects_found?(issuable_id, user_id)
set_user(user_id)
set_issuable(issuable_id)
user && issuable
end
def set_user(user_id)
@user = User.find_by(id: user_id)
unless @user
log_error(User, user_id)
return false
log_error(User, user_id) unless @user
end
true
def set_issuable(issuable_id)
@issuable = issuable_class.find_by(id: issuable_id)
log_error(issuable_class, issuable_id) unless @issuable
end
def log_error(record_class, record_id)
......
......@@ -4,7 +4,7 @@ class NewIssueWorker
include NewIssuable
def perform(issue_id, user_id)
return unless ensure_objects_found(issue_id, user_id)
return unless objects_found?(issue_id, user_id)
EventCreateService.new.open_issue(issuable, user)
NotificationService.new.new_issue(issuable, user)
......
......@@ -4,7 +4,7 @@ class NewMergeRequestWorker
include NewIssuable
def perform(merge_request_id, user_id)
return unless ensure_objects_found(merge_request_id, user_id)
return unless objects_found?(merge_request_id, user_id)
EventCreateService.new.open_mr(issuable, user)
NotificationService.new.new_merge_request(issuable, user)
......
......@@ -4,7 +4,7 @@ class PagesWorker
sidekiq_options queue: :pages, retry: false
def perform(action, *arg)
send(action, *arg)
send(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
end
def deploy(build_id)
......
#!/usr/bin/env ruby
require 'bundler/setup'
require 'stackprof'
$:.unshift 'spec'
require 'rails_helper'
......@@ -13,4 +14,4 @@ StackProf.run(mode: :wall, out: output_file, interval: interval) do
RSpec::Core::Runner.run(ARGV, $stderr, $stdout)
end
system("stackprof #{output_file} --text --limit #{limit}")
system("bundle exec stackprof #{output_file} --text --limit #{limit}")
---
title: Expose noteable_iid in Note
merge_request: 13265
author: sue445
---
title: Use full path of user's avatar in webhooks
merge_request: 13401
author: Vitaliy @blackst0ne Klachkov
---
title: improve file upload/replace experience
merge_request:
author:
---
title: Raise guessed encoding confidence threshold to 50
merge_request: 12990
author:
---
title: Fix bar chart does not display label at 0 hour
merge_request: 35136
author: Jason Dai
---
title: Add checks for branch existence before changing HEAD
merge_request: 13359
author: Vitaliy @blackst0ne Klachkov
---
title: Simplify checking if objects exist code in new issaubles workers
merge_request:
author:
---
title: Add missing validation error for username change with container registry tags
merge_request: 13356
author:
---
title: Fix the /projects/:id/repository/commits endpoint to handle dots in the ref
name when the project full path contains a `/`
merge_request: 13370
author:
......@@ -176,7 +176,7 @@ module Gitlab
next unless name.include?('namespace_project')
define_method(name.sub('namespace_project', 'project')) do |project, *args|
send(name, project&.namespace, project, *args)
send(name, project&.namespace, project, *args) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
......
......@@ -71,7 +71,7 @@ class Settings < Settingslogic
# check that `current` (string or integer) is a contant in `modul`.
def verify_constant(modul, current, default)
constant = modul.constants.find{ |name| modul.const_get(name) == current }
constant = modul.constants.find { |name| modul.const_get(name) == current }
value = constant.nil? ? default : modul.const_get(constant)
if current.is_a? String
value = modul.const_get(current.upcase) rescue default
......
......@@ -18,7 +18,7 @@ module ActiveRecord
lock_col = self.class.locking_column
previous_lock_value = send(lock_col).to_i
previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
# This line is added as a patch
previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
......@@ -48,7 +48,7 @@ module ActiveRecord
# If something went wrong, revert the version.
rescue Exception
send(lock_col + '=', previous_lock_value)
send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
raise
end
end
......
......@@ -10,7 +10,7 @@ class AddUniqueIndexToLabels < ActiveRecord::Migration
def up
select_all('SELECT title, project_id, COUNT(id) as cnt FROM labels GROUP BY project_id, title HAVING COUNT(id) > 1').each do |label|
label_title = quote_string(label['title'])
duplicated_ids = select_all("SELECT id FROM labels WHERE project_id = #{label['project_id']} AND title = '#{label_title}' ORDER BY id ASC").map{ |label| label['id'] }
duplicated_ids = select_all("SELECT id FROM labels WHERE project_id = #{label['project_id']} AND title = '#{label_title}' ORDER BY id ASC").map { |label| label['id'] }
label_id = duplicated_ids.first
duplicated_ids.delete(label_id)
......
......@@ -30,7 +30,7 @@ class CleanupNamespacelessPendingDeleteProjects < ActiveRecord::Migration
private
def pending_delete_batch
connection.exec_query(find_batch).map{ |row| row['id'].to_i }
connection.exec_query(find_batch).map { |row| row['id'].to_i }
end
BATCH_SIZE = 5000
......
......@@ -3,7 +3,7 @@
Welcome to [GitLab](https://about.gitlab.com/), a Git-based fully featured
platform for software development!
We offer four different products for you and your company:
GitLab offers the most scalable Git-based fully integrated platform for software development, with flexible products and subscription plans:
- **GitLab Community Edition (CE)** is an [opensource product](https://gitlab.com/gitlab-org/gitlab-ce/),
self-hosted, free to use. Every feature available in GitLab CE is also available on GitLab Enterprise Edition (Starter and Premium) and GitLab.com.
......
......@@ -9,6 +9,33 @@ documentation](http://docs.gitlab.com/ee/administration/audit_events.html)
System log files are typically plain text in a standard log file format.
This guide talks about how to read and use these system log files.
## `production_json.log`
This file lives in `/var/log/gitlab/gitlab-rails/production_json.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/production_json.log` for
installations from source. (When Gitlab is running in an environment
other than production, the corresponding logfile is shown here.)
It contains a structured log for Rails controller requests received from
GitLab, thanks to [Lograge](https://github.com/roidrage/lograge/). Note that
requests from the API [are not yet logged to this
file](https://gitlab.com/gitlab-org/gitlab-ce/issues/36189).
Each line contains a JSON line that can be ingested by Elasticsearch, Splunk, etc. For example:
```json
{"method":"GET","path":"/gitlab/gitlab-ce/issues/1234","format":"html","controller":"Projects::IssuesController","action":"show","status":200,"duration":229.03,"view":174.07,"db":13.24,"time":"2017-08-08T20:15:54.821Z","params":{"namespace_id":"gitlab","project_id":"gitlab-ce","id":"1234"},"remote_ip":"18.245.0.1","user_id":1,"username":"admin"}
```
In this example, you can see this was a GET request for a specific issue. Notice each line also contains performance data:
1. `duration`: the total time taken to retrieve the request
2. `view`: total time taken inside the Rails views
3. `db`: total time to retrieve data from the database
In addition, the log contains the IP address from which the request originated
(`remote_ip`) as well as the user's ID (`user_id`), and username (`username`).
## `production.log`
This file lives in `/var/log/gitlab/gitlab-rails/production.log` for
......
......@@ -338,6 +338,45 @@ Example response:
"web_url":"https://gitlab.example.com/ted"
},
"author_username":"ted"
},
{
"title": null,
"project_id": 1,
"action_name": "commented on",
"target_id": 1312,
"target_iid": 1312,
"target_type": "Note",
"author_id": 1,
"data": null,
"target_title": null,
"created_at": "2015-12-04T10:33:58.089Z",
"note": {
"id": 1312,
"body": "What an awesome day!",
"attachment": null,
"author": {
"name": "Dmitriy Zaporozhets",
"username": "root",
"id": 1,
"state": "active",
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
"web_url": "http://localhost:3000/root"
},
"created_at": "2015-12-04T10:33:56.698Z",
"system": false,
"noteable_id": 377,
"noteable_type": "Issue",
"noteable_iid": 377
},
"author": {
"name": "Dmitriy Zaporozhets",
"username": "root",
"id": 1,
"state": "active",
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png",
"web_url": "http://localhost:3000/root"
},
"author_username": "root"
}
]
```
......
......@@ -35,7 +35,8 @@ Parameters:
"updated_at": "2013-10-02T10:22:45Z",
"system": true,
"noteable_id": 377,
"noteable_type": "Issue"
"noteable_type": "Issue",
"noteable_iid": 377
},
{
"id": 305,
......@@ -53,7 +54,8 @@ Parameters:
"updated_at": "2013-10-02T09:56:03Z",
"system": true,
"noteable_id": 121,
"noteable_type": "Issue"
"noteable_type": "Issue",
"noteable_iid": 121
}
]
```
......@@ -267,7 +269,8 @@ Parameters:
"updated_at": "2013-10-02T08:57:14Z",
"system": false,
"noteable_id": 2,
"noteable_type": "MergeRequest"
"noteable_type": "MergeRequest",
"noteable_iid": 2
}
```
......
......@@ -296,9 +296,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 9-4-stable gitlab
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 9-5-stable gitlab
**Note:** You can change `9-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
**Note:** You can change `9-5-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
......@@ -507,15 +507,17 @@ Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
### Compile GetText PO files
sudo -u git -H bundle exec rake gettext:pack RAILS_ENV=production
sudo -u git -H bundle exec rake gettext:po_to_json RAILS_ENV=production
### Compile Assets
sudo -u git -H yarn install --production --pure-lockfile
sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
### Compile GetText PO files
sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
### Start Your GitLab Instance
sudo service gitlab start
......
......@@ -31,7 +31,7 @@ X-Gitlab-Event: System Hook
"path": "storecloud",
"path_with_namespace": "jsmith/storecloud",
"project_id": 74,
"project_visibility": "private",
"project_visibility": "private"
}
```
......@@ -48,7 +48,7 @@ X-Gitlab-Event: System Hook
"path": "underscore",
"path_with_namespace": "jsmith/underscore",
"project_id": 73,
"project_visibility": "internal",
"project_visibility": "internal"
}
```
......@@ -66,7 +66,7 @@ X-Gitlab-Event: System Hook
"owner_name": "John Smith",
"owner_email": "johnsmith@gmail.com",
"project_visibility": "internal",
"old_path_with_namespace": "jsmith/overscore",
"old_path_with_namespace": "jsmith/overscore"
}
```
......@@ -84,7 +84,7 @@ X-Gitlab-Event: System Hook
"owner_name": "John Smith",
"owner_email": "johnsmith@gmail.com",
"project_visibility": "internal",
"old_path_with_namespace": "jsmith/overscore",
"old_path_with_namespace": "jsmith/overscore"
}
```
......@@ -101,7 +101,7 @@ X-Gitlab-Event: System Hook
"path": "storecloud",
"path_with_namespace": "jsmith/storecloud",
"project_id": 74,
"project_visibility": "private",
"project_visibility": "private"
}
```
......@@ -121,7 +121,7 @@ X-Gitlab-Event: System Hook
"user_name": "John Smith",
"user_username": "johnsmith",
"user_id": 41,
"project_visibility": "private",
"project_visibility": "private"
}
```
......@@ -141,7 +141,7 @@ X-Gitlab-Event: System Hook
"user_name": "John Smith",
"user_username": "johnsmith",
"user_id": 41,
"project_visibility": "private",
"project_visibility": "private"
}
```
......
......@@ -100,6 +100,7 @@ cd /home/git/gitlab
sudo -u git -H git fetch --all
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
sudo -u git -H git checkout -- locale
```
For GitLab Community Edition:
......@@ -236,6 +237,11 @@ sudo -u git -H bundle clean
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Compile GetText PO files
sudo -u git -H bundle exec rake gettext:pack RAILS_ENV=production
sudo -u git -H bundle exec rake gettext:po_to_json RAILS_ENV=production
# Update node dependencies and recompile assets
sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
......
......@@ -100,6 +100,7 @@ cd /home/git/gitlab
sudo -u git -H git fetch --all
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
sudo -u git -H git checkout -- locale
```
For GitLab Community Edition:
......@@ -272,6 +273,10 @@ sudo -u git -H bundle clean
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Compile GetText PO files
sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
# Update node dependencies and recompile assets
sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
......
......@@ -100,6 +100,7 @@ cd /home/git/gitlab
sudo -u git -H git fetch --all
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
sudo -u git -H git checkout -- locale
```
For GitLab Community Edition:
......@@ -285,6 +286,10 @@ sudo -u git -H bundle clean
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Compile GetText PO files
sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
# Update node dependencies and recompile assets
sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
......
......@@ -35,7 +35,7 @@ current version with `cat VERSION`).
cd /home/git/gitlab
sudo -u git -H git fetch --all
sudo -u git -H git checkout -- Gemfile.lock db/schema.rb
sudo -u git -H git checkout -- Gemfile.lock db/schema.rb locale
sudo -u git -H git checkout LATEST_TAG -b LATEST_TAG
```
......@@ -56,11 +56,21 @@ sudo -u git -H bundle clean
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
### 4. Compile GetText PO files
Internationalization was added in `v9.2.0` so these commands are only
required for versions equal or major to it.
```bash
sudo -u git -H bundle exec rake gettext:pack RAILS_ENV=production
sudo -u git -H bundle exec rake gettext:po_to_json RAILS_ENV=production
```
# Clean up assets and cache
sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production NODE_ENV=production
```
### 4. Update gitlab-workhorse to the corresponding version
### 5. Update gitlab-workhorse to the corresponding version
```bash
cd /home/git/gitlab
......@@ -68,7 +78,7 @@ cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
```
### 5. Update gitlab-shell to the corresponding version
### 6. Update gitlab-shell to the corresponding version
```bash
cd /home/git/gitlab-shell
......@@ -78,14 +88,14 @@ sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`ca
sudo -u git -H sh -c 'if [ -x bin/compile ]; then bin/compile; fi'
```
### 6. Start application
### 7. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
### 7. Check application status
### 8. Check application status
Check if GitLab and its environment are configured correctly:
......
......@@ -4,13 +4,14 @@ module API
class Commits < Grape::API
include PaginationParams
before { authenticate! }
COMMIT_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(sha: API::NO_SLASH_URL_PART_REGEX)
before { authorize! :download_code, user_project }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: { id: %r{[^/]+} } do
resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a project repository commits' do
success Entities::RepoCommit
end
......@@ -21,7 +22,7 @@ module API
optional :path, type: String, desc: 'The file path'
use :pagination
end
get ":id/repository/commits" do
get ':id/repository/commits' do
path = params[:path]
before = params[:until]
after = params[:since]
......@@ -60,7 +61,7 @@ module API
optional :author_email, type: String, desc: 'Author email for commit'
optional :author_name, type: String, desc: 'Author name for commit'
end
post ":id/repository/commits" do
post ':id/repository/commits' do
authorize! :push_code, user_project
attrs = declared_params
......@@ -79,42 +80,42 @@ module API
desc 'Get a specific commit of a project' do
success Entities::RepoCommitDetail
failure [[404, 'Not Found']]
failure [[404, 'Commit Not Found']]
end
params do
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
get ":id/repository/commits/:sha" do
get ':id/repository/commits/:sha', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! "Commit" unless commit
not_found! 'Commit' unless commit
present commit, with: Entities::RepoCommitDetail
end
desc 'Get the diff for a specific commit of a project' do
failure [[404, 'Not Found']]
failure [[404, 'Commit Not Found']]
end
params do
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
get ":id/repository/commits/:sha/diff" do
get ':id/repository/commits/:sha/diff', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! "Commit" unless commit
not_found! 'Commit' unless commit
commit.raw_diffs.to_a
end
desc "Get a commit's comments" do
success Entities::CommitNote
failure [[404, 'Not Found']]
failure [[404, 'Commit Not Found']]
end
params do
use :pagination
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
end
get ':id/repository/commits/:sha/comments' do
get ':id/repository/commits/:sha/comments', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
......@@ -128,10 +129,10 @@ module API
success Entities::RepoCommit
end
params do
requires :sha, type: String, desc: 'A commit sha to be cherry picked'
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag to be cherry picked'
requires :branch, type: String, desc: 'The name of the branch'
end
post ':id/repository/commits/:sha/cherry_pick' do
post ':id/repository/commits/:sha/cherry_pick', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
authorize! :push_code, user_project
commit = user_project.commit(params[:sha])
......@@ -160,7 +161,7 @@ module API
success Entities::CommitNote
end
params do
requires :sha, type: String, regexp: /\A\h{6,40}\z/, desc: "The commit's SHA"
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag on which to post a comment'
requires :note, type: String, desc: 'The text of the comment'
optional :path, type: String, desc: 'The file path'
given :path do
......@@ -168,7 +169,7 @@ module API
requires :line_type, type: String, values: %w(new old), default: 'new', desc: 'The type of the line'
end
end
post ':id/repository/commits/:sha/comments' do
post ':id/repository/commits/:sha/comments', requirements: COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
......
......@@ -132,7 +132,7 @@ module API
expose :lfs_enabled?, as: :lfs_enabled
expose :creator_id
expose :namespace, using: 'API::Entities::Namespace'
expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? }
expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? }
expose :import_status
expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :avatar_url do |user, options|
......@@ -454,6 +454,9 @@ module API
end
class Note < Grape::Entity
# Only Issue and MergeRequest have iid
NOTEABLE_TYPES_WITH_IID = %w(Issue MergeRequest).freeze
expose :id
expose :note, as: :body
expose :attachment_identifier, as: :attachment
......@@ -461,6 +464,9 @@ module API
expose :created_at, :updated_at
expose :system?, as: :system
expose :noteable_id, :noteable_type
# Avoid N+1 queries as much as possible
expose(:noteable_iid) { |note| note.noteable.iid if NOTEABLE_TYPES_WITH_IID.include?(note.noteable_type) }
end
class AwardEmoji < Grape::Entity
......
# rubocop:disable GitlabSecurity/PublicSend
module API
module Helpers
module MembersHelpers
def find_source(source_type, id)
public_send("find_#{source_type}!", id)
public_send("find_#{source_type}!", id) # rubocop:disable GitlabSecurity/PublicSend
end
def authorize_admin_source!(source_type, source)
......
......@@ -139,7 +139,7 @@ module API
helpers do
def find_project_noteable(noteables_str, noteable_id)
public_send("find_project_#{noteables_str.singularize}", noteable_id)
public_send("find_project_#{noteables_str.singularize}", noteable_id) # rubocop:disable GitlabSecurity/PublicSend
end
def noteable_read_ability_name(noteable)
......
......@@ -68,7 +68,7 @@ module API
expose :lfs_enabled?, as: :lfs_enabled
expose :creator_id
expose :namespace, using: 'API::Entities::Namespace'
expose :forked_from_project, using: ::API::Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? }
expose :forked_from_project, using: ::API::Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? }
expose :avatar_url do |user, options|
user.avatar_url(only_path: false)
end
......
......@@ -198,11 +198,11 @@ module Backup
end
def archives_to_backup
ARCHIVES_TO_BACKUP.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact
ARCHIVES_TO_BACKUP.map { |name| (name + ".tar.gz") unless skipped?(name) }.compact
end
def folders_to_backup
FOLDERS_TO_BACKUP.reject{ |name| skipped?(name) }
FOLDERS_TO_BACKUP.reject { |name| skipped?(name) }
end
def disabled_features
......
......@@ -254,7 +254,7 @@ module Ci
def state
state = STATE_PARAMS.inject({}) do |h, param|
h[param] = send(param)
h[param] = send(param) # rubocop:disable GitlabSecurity/PublicSend
h
end
Base64.urlsafe_encode64(state.to_json)
......@@ -266,7 +266,7 @@ module Ci
return if state[:offset].to_i > stream.size
STATE_PARAMS.each do |param|
send("#{param}=".to_sym, state[param])
send("#{param}=".to_sym, state[param]) # rubocop:disable GitlabSecurity/PublicSend
end
end
......
......@@ -47,7 +47,7 @@ module Ci
def collect
query = project.pipelines
.where("? > #{Ci::Pipeline.table_name}.created_at AND #{Ci::Pipeline.table_name}.created_at > ?", @to, @from)
.where("? > #{Ci::Pipeline.table_name}.created_at AND #{Ci::Pipeline.table_name}.created_at > ?", @to, @from) # rubocop:disable GitlabSecurity/SqlInjection
totals_count = grouped_count(query)
success_count = grouped_count(query.success)
......
......@@ -6,6 +6,8 @@ class ProjectUrlConstrainer
return false unless DynamicPathValidator.valid_project_path?(full_path)
# We intentionally allow SELECT(*) here so result of this query can be used
# as cache for further Project.find_by_full_path calls within request
Project.find_by_full_path(full_path, follow_redirects: request.get?).present?
end
end
......@@ -77,8 +77,8 @@ EOM
def initialize(merge_request, project)
@merge_request = merge_request
@our_commit = merge_request.source_branch_head.raw.raw_commit
@their_commit = merge_request.target_branch_head.raw.raw_commit
@our_commit = merge_request.source_branch_head.raw.rugged_commit
@their_commit = merge_request.target_branch_head.raw.rugged_commit
@project = project
end
end
......
......@@ -54,7 +54,7 @@ module Gitlab
end
def serialize_commit(event, commit, query)
commit = Commit.new(Gitlab::Git::Commit.new(commit.to_hash), @project)
commit = Commit.from_hash(commit.to_hash, @project)
AnalyticsCommitSerializer.new(project: @project, total_time: event['total_time']).represent(commit)
end
......
......@@ -21,7 +21,7 @@ module Gitlab
def to_hash
hash = {}
serialize_keys.each { |key| hash[key] = send(key) }
serialize_keys.each { |key| hash[key] = send(key) } # rubocop:disable GitlabSecurity/PublicSend
hash
end
......
......@@ -98,10 +98,11 @@ module Gitlab
if status.zero?
@ee_branch_found = ee_branch_prefix
else
_, status = step("Fetching origin/#{ee_branch_suffix}", %W[git fetch origin #{ee_branch_suffix}])
return
end
_, status = step("Fetching origin/#{ee_branch_suffix}", %W[git fetch origin #{ee_branch_suffix}])
if status.zero?
@ee_branch_found = ee_branch_suffix
else
......
......@@ -11,7 +11,7 @@ module Gitlab
# obscure encoding with low confidence.
# There is a lot more info with this merge request:
# https://gitlab.com/gitlab-org/gitlab_git/merge_requests/77#note_4754193
ENCODING_CONFIDENCE_THRESHOLD = 40
ENCODING_CONFIDENCE_THRESHOLD = 50
def encode!(message)
return nil unless message.respond_to? :force_encoding
......
......@@ -16,7 +16,7 @@ module Gitlab
def each
@blames.each do |blame|
yield(
Gitlab::Git::Commit.new(blame.commit),
Gitlab::Git::Commit.new(@repo, blame.commit),
blame.line
)
end
......
......@@ -14,7 +14,7 @@ module Gitlab
attr_accessor *SERIALIZE_KEYS # rubocop:disable Lint/AmbiguousOperator
delegate :tree, to: :raw_commit
delegate :tree, to: :rugged_commit
def ==(other)
return false unless other.is_a?(Gitlab::Git::Commit)
......@@ -50,19 +50,29 @@ module Gitlab
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/321
def find(repo, commit_id = "HEAD")
# Already a commit?
return commit_id if commit_id.is_a?(Gitlab::Git::Commit)
return decorate(commit_id) if commit_id.is_a?(Rugged::Commit)
obj = if commit_id.is_a?(String)
repo.rev_parse_target(commit_id)
# A rugged reference?
commit_id = Gitlab::Git::Ref.dereference_object(commit_id)
return decorate(repo, commit_id) if commit_id.is_a?(Rugged::Commit)
# Some weird thing?
return nil unless commit_id.is_a?(String)
commit = repo.gitaly_migrate(:find_commit) do |is_enabled|
if is_enabled
repo.gitaly_commit_client.find_commit(commit_id)
else
Gitlab::Git::Ref.dereference_object(commit_id)
end
obj = repo.rev_parse_target(commit_id)
return nil unless obj.is_a?(Rugged::Commit)
obj.is_a?(Rugged::Commit) ? obj : nil
end
end
decorate(obj)
rescue Rugged::ReferenceError, Rugged::InvalidError, Rugged::ObjectError, Gitlab::Git::Repository::NoRepository
decorate(repo, commit) if commit
rescue Rugged::ReferenceError, Rugged::InvalidError, Rugged::ObjectError,
Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository
nil
end
......@@ -102,7 +112,7 @@ module Gitlab
if is_enabled
repo.gitaly_commit_client.between(base, head)
else
repo.rugged_commits_between(base, head).map { |c| decorate(c) }
repo.rugged_commits_between(base, head).map { |c| decorate(repo, c) }
end
end
rescue Rugged::ReferenceError
......@@ -169,7 +179,7 @@ module Gitlab
offset = actual_options[:skip]
limit = actual_options[:max_count]
walker.each(offset: offset, limit: limit) do |commit|
commits.push(decorate(commit))
commits.push(decorate(repo, commit))
end
walker.reset
......@@ -183,27 +193,8 @@ module Gitlab
Gitlab::GitalyClient::CommitService.new(repo).find_all_commits(options)
end
def decorate(commit, ref = nil)
Gitlab::Git::Commit.new(commit, ref)
end
# Returns a diff object for the changes introduced by +rugged_commit+.
# If +rugged_commit+ doesn't have a parent, then the diff is between
# this commit and an empty repo. See Repository#diff for the keys
# allowed in the +options+ hash.
def diff_from_parent(rugged_commit, options = {})
options ||= {}
break_rewrites = options[:break_rewrites]
actual_options = Gitlab::Git::Diff.filter_diff_options(options)
diff = if rugged_commit.parents.empty?
rugged_commit.diff(actual_options.merge(reverse: true))
else
rugged_commit.parents[0].diff(rugged_commit, actual_options)
end
diff.find_similar!(break_rewrites: break_rewrites)
diff
def decorate(repository, commit, ref = nil)
Gitlab::Git::Commit.new(repository, commit, ref)
end
# Returns the `Rugged` sorting type constant for one or more given
......@@ -221,7 +212,7 @@ module Gitlab
end
end
def initialize(raw_commit, head = nil)
def initialize(repository, raw_commit, head = nil)
raise "Nil as raw commit passed" unless raw_commit
case raw_commit
......@@ -229,12 +220,13 @@ module Gitlab
init_from_hash(raw_commit)
when Rugged::Commit
init_from_rugged(raw_commit)
when Gitlab::GitalyClient::Commit
when Gitaly::GitCommit
init_from_gitaly(raw_commit)
else
raise "Invalid raw commit type: #{raw_commit.class}"
end
@repository = repository
@head = head
end
......@@ -269,7 +261,7 @@ module Gitlab
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/324
def to_diff
diff_from_parent.patch
rugged_diff_from_parent.patch
end
# Returns a diff object for the changes from this commit's first parent.
......@@ -277,11 +269,42 @@ module Gitlab
# empty repo. See Repository#diff for keys allowed in the +options+
# hash.
def diff_from_parent(options = {})
Commit.diff_from_parent(raw_commit, options)
Gitlab::GitalyClient.migrate(:commit_raw_diffs) do |is_enabled|
if is_enabled
@repository.gitaly_commit_client.diff_from_parent(self, options)
else
rugged_diff_from_parent(options)
end
end
end
def rugged_diff_from_parent(options = {})
options ||= {}
break_rewrites = options[:break_rewrites]
actual_options = Gitlab::Git::Diff.filter_diff_options(options)
diff = if rugged_commit.parents.empty?
rugged_commit.diff(actual_options.merge(reverse: true))
else
rugged_commit.parents[0].diff(rugged_commit, actual_options)
end
diff.find_similar!(break_rewrites: break_rewrites)
diff
end
def deltas
@deltas ||= diff_from_parent.each_delta.map { |d| Gitlab::Git::Diff.new(d) }
@deltas ||= begin
deltas = Gitlab::GitalyClient.migrate(:commit_deltas) do |is_enabled|
if is_enabled
@repository.gitaly_commit_client.commit_deltas(self)
else
rugged_diff_from_parent.each_delta
end
end
deltas.map { |delta| Gitlab::Git::Diff.new(delta) }
end
end
def has_zero_stats?
......@@ -296,7 +319,7 @@ module Gitlab
def to_hash
serialize_keys.map.with_object({}) do |key, hash|
hash[key] = send(key)
hash[key] = send(key) # rubocop:disable GitlabSecurity/PublicSend
end
end
......@@ -309,14 +332,7 @@ module Gitlab
end
def parents
case raw_commit
when Rugged::Commit
raw_commit.parents.map { |c| Gitlab::Git::Commit.new(c) }
when Gitlab::GitalyClient::Commit
parent_ids.map { |oid| self.class.find(raw_commit.repository, oid) }.compact
else
raise NotImplementedError, "commit source doesn't support #parents"
end
parent_ids.map { |oid| self.class.find(@repository, oid) }.compact
end
# Get the gpg signature of this commit.
......@@ -334,7 +350,7 @@ module Gitlab
def to_patch(options = {})
begin
raw_commit.to_mbox(options)
rugged_commit.to_mbox(options)
rescue Rugged::InvalidError => ex
if ex.message =~ /commit \w+ is a merge commit/i
'Patch format is not currently supported for merge commits.'
......@@ -382,13 +398,21 @@ module Gitlab
encode! @committer_email
end
def rugged_commit
@rugged_commit ||= if raw_commit.is_a?(Rugged::Commit)
raw_commit
else
@repository.rev_parse_target(id)
end
end
private
def init_from_hash(hash)
raw_commit = hash.symbolize_keys
serialize_keys.each do |key|
send("#{key}=", raw_commit[key])
send("#{key}=", raw_commit[key]) # rubocop:disable GitlabSecurity/PublicSend
end
end
......@@ -415,10 +439,10 @@ module Gitlab
# subject from the message to make it clearer when there's one
# available but not the other.
@message = (commit.body.presence || commit.subject).dup
@authored_date = Time.at(commit.author.date.seconds)
@authored_date = Time.at(commit.author.date.seconds).utc
@author_name = commit.author.name.dup
@author_email = commit.author.email.dup
@committed_date = Time.at(commit.committer.date.seconds)
@committed_date = Time.at(commit.committer.date.seconds).utc
@committer_name = commit.committer.name.dup
@committer_email = commit.committer.email.dup
@parent_ids = commit.parent_ids
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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