Commit 3e28cca5 authored by Mek Stittri's avatar Mek Stittri

Resolve conflicts with latest workflow label changes

parents acdc0d73 abc6be62
...@@ -815,8 +815,6 @@ lint:javascript:report: ...@@ -815,8 +815,6 @@ lint:javascript:report:
- setup-test-env - setup-test-env
before_script: [] before_script: []
script: script:
- date
- find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
- date - date
- yarn run eslint-report || true # ignore exit code - yarn run eslint-report || true # ignore exit code
artifacts: artifacts:
......
...@@ -27,25 +27,26 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._ ...@@ -27,25 +27,26 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._
- [Helping others](#helping-others) - [Helping others](#helping-others)
- [I want to contribute!](#i-want-to-contribute) - [I want to contribute!](#i-want-to-contribute)
- [Workflow labels](#workflow-labels) - [Workflow labels](#workflow-labels)
- [Type labels (~"feature proposal", ~bug, ~customer, etc.)](#type-labels-feature-proposal-bug-customer-etc) - [Type labels](#type-labels)
- [Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.)](#subject-labels-wiki-container-registry-ldap-api-etc) - [Subject labels](#subject-labels)
- [Team labels (~"CI/CD", ~Discussion, ~Quality, ~Platform, etc.)](#team-labels-cicd-discussion-quality-platform-etc) - [Team labels](#team-labels)
- [Release Scoping labels (~Deliverable, ~Stretch, ~"Next Patch Release")](#milestone-labels-deliverable-stretch-next-patch-release) - [Release Scoping labels](#release-scoping-labels)
- [Priority labels (~P1, ~P2, ~P3 , ~P4)](#bug-priority-labels-p1-p2-p3-p4) - [Bug Priority labels](#bug-priority-labels)
- [Severity labels (~S1, ~S2, ~S3 , ~S4)](#bug-severity-labels-s1-s2-s3-s4) - [Bug Severity labels](#bug-severity-labels)
- [Label for community contributors (~"Accepting Merge Requests")](#label-for-community-contributors-accepting-merge-requests) - [Severity impact guidance](#severity-impact-guidance)
- [Implement design & UI elements](#implement-design--ui-elements) - [Label for community contributors](#label-for-community-contributors)
- [Implement design & UI elements](#implement-design-ui-elements)
- [Issue tracker](#issue-tracker) - [Issue tracker](#issue-tracker)
- [Issue triaging](#issue-triaging) - [Issue triaging](#issue-triaging)
- [Feature proposals](#feature-proposals) - [Feature proposals](#feature-proposals)
- [Issue tracker guidelines](#issue-tracker-guidelines) - [Issue tracker guidelines](#issue-tracker-guidelines)
- [Issue weight](#issue-weight) - [Issue weight](#issue-weight)
- [Regression issues](#regression-issues) - [Regression issues](#regression-issues)
- [Technical and UX debt](#technical-and-ux-debt) - [Technical and UX debt](#technical-and-ux-debt)
- [Stewardship](#stewardship) - [Stewardship](#stewardship)
- [Merge requests](#merge-requests) - [Merge requests](#merge-requests)
- [Merge request guidelines](#merge-request-guidelines) - [Merge request guidelines](#merge-request-guidelines)
- [Contribution acceptance criteria](#contribution-acceptance-criteria) - [Contribution acceptance criteria](#contribution-acceptance-criteria)
- [Definition of done](#definition-of-done) - [Definition of done](#definition-of-done)
- [Style guides](#style-guides) - [Style guides](#style-guides)
- [Code of conduct](#code-of-conduct) - [Code of conduct](#code-of-conduct)
...@@ -145,7 +146,7 @@ labels, you can _always_ add the team and type, and often also the subject. ...@@ -145,7 +146,7 @@ labels, you can _always_ add the team and type, and often also the subject.
[milestones-page]: https://gitlab.com/gitlab-org/gitlab-ce/milestones [milestones-page]: https://gitlab.com/gitlab-org/gitlab-ce/milestones
[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels [labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels
### Type labels (~"feature proposal", ~bug, ~customer, etc.) ### Type labels
Type labels are very important. They define what kind of issue this is. Every Type labels are very important. They define what kind of issue this is. Every
issue should have one or more. issue should have one or more.
...@@ -161,28 +162,41 @@ already reserved for subject labels). ...@@ -161,28 +162,41 @@ already reserved for subject labels).
The descriptions on the [labels page][labels-page] explain what falls under each type label. The descriptions on the [labels page][labels-page] explain what falls under each type label.
### Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.) ### Subject labels
Subject labels are labels that define what area or feature of GitLab this issue Subject labels are labels that define what area or feature of GitLab this issue
hits. They are not always necessary, but very convenient. hits. They are not always necessary, but very convenient.
Examples of subject labels are ~wiki, ~ldap, ~api,
~issues, ~"merge requests", ~labels, and ~"container registry".
If you are an expert in a particular area, it makes it easier to find issues to If you are an expert in a particular area, it makes it easier to find issues to
work on. You can also subscribe to those labels to receive an email each time an work on. You can also subscribe to those labels to receive an email each time an
issue is labeled with a subject label corresponding to your expertise. issue is labeled with a subject label corresponding to your expertise.
Examples of subject labels are ~wiki, ~"container registry", ~ldap, ~api,
~issues, ~"merge requests", ~labels, and ~"container registry".
Subject labels are always all-lowercase. Subject labels are always all-lowercase.
### Team labels (~"CI/CD", ~Discussion, ~Quality, ~Platform, etc.) ### Team labels
Team labels specify what team is responsible for this issue. Team labels specify what team is responsible for this issue.
Assigning a team label makes sure issues get the attention of the appropriate Assigning a team label makes sure issues get the attention of the appropriate
people. people.
The current team labels are ~Distribution, ~"CI/CD", ~Discussion, ~Documentation, ~Quality, The current team labels are:
~Geo, ~Gitaly, ~Monitoring, ~Platform, ~Release, ~"Security Products", ~"Configuration", and ~"UX".
- ~Configuration
- ~"CI/CD"
- ~Discussion
- ~Distribution
- ~Documentation
- ~Geo
- ~Gitaly
- ~Monitoring
- ~Platform
- ~Quality
- ~Release
- ~"Security Products"
- ~UX
The descriptions on the [labels page][labels-page] explain what falls under the The descriptions on the [labels page][labels-page] explain what falls under the
responsibility of each team. responsibility of each team.
...@@ -193,7 +207,7 @@ indicate if an issue needs backend work, frontend work, or both. ...@@ -193,7 +207,7 @@ indicate if an issue needs backend work, frontend work, or both.
Team labels are always capitalized so that they show up as the first label for Team labels are always capitalized so that they show up as the first label for
any issue. any issue.
### Release Scoping labels (~Deliverable, ~Stretch, ~"Next Patch Release") ### Release Scoping labels
Release Scoping labels help us clearly communicate expectations of the work for the Release Scoping labels help us clearly communicate expectations of the work for the
release. There are three levels of Release Scoping labels: release. There are three levels of Release Scoping labels:
...@@ -211,9 +225,9 @@ Each issue scheduled for the current milestone should be labeled ~Deliverable ...@@ -211,9 +225,9 @@ Each issue scheduled for the current milestone should be labeled ~Deliverable
or ~"Stretch". Any open issue for a previous milestone should be labeled or ~"Stretch". Any open issue for a previous milestone should be labeled
~"Next Patch Release", or otherwise rescheduled to a different milestone. ~"Next Patch Release", or otherwise rescheduled to a different milestone.
### Bug Priority labels (~P1, ~P2, ~P3, ~P4) ### Bug Priority labels
Bug Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be. Bug Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
If there are multiple defects, the priority decides which defect has to be fixed immediately versus later. If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
This label documents the planned timeline & urgency which is used to measure against our actual SLA on delivering ~bug fixes. This label documents the planned timeline & urgency which is used to measure against our actual SLA on delivering ~bug fixes.
...@@ -224,7 +238,7 @@ This label documents the planned timeline & urgency which is used to measure aga ...@@ -224,7 +238,7 @@ This label documents the planned timeline & urgency which is used to measure aga
| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) | | | ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) | |
| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) | The issue is prominent but does not impact user workflow and a workaround is documented | | ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) | The issue is prominent but does not impact user workflow and a workaround is documented |
### Bug Severity labels (~S1, ~S2, ~S3, ~S4) ### Bug Severity labels
Severity labels help us clearly communicate the impact of a ~bug on users. Severity labels help us clearly communicate the impact of a ~bug on users.
...@@ -240,11 +254,11 @@ Severity labels help us clearly communicate the impact of a ~bug on users. ...@@ -240,11 +254,11 @@ Severity labels help us clearly communicate the impact of a ~bug on users.
| Label | Security Impact | Availability / Performance Impact | | Label | Security Impact | Availability / Performance Impact |
|-------|---------------------------------------------------------------------|--------------------------------------------------------------| |-------|---------------------------------------------------------------------|--------------------------------------------------------------|
| ~S1 | >50% users impacted (possible company extinction level event) | | | ~S1 | >50% users impacted (possible company extinction level event) | |
| ~S2 | Many users or multiple paid customers impacted (but not apocalyptic)| The issue is (almost) guaranteed to occur in the near future | | ~S2 | Many users or multiple paid customers impacted (but not apocalyptic)| The issue is (almost) guaranteed to occur in the near future |
| ~S3 | A few users or a single paid customer impacted | The issue is likely to occur in the near future | | ~S3 | A few users or a single paid customer impacted | The issue is likely to occur in the near future |
| ~S4 | No paid users/customer impacted, or expected impact within 30 days | The issue _may_ occur but it's not likely | | ~S4 | No paid users/customer impacted, or expected impact within 30 days | The issue _may_ occur but it's not likely |
### Label for community contributors (~"Accepting Merge Requests") ### Label for community contributors
Issues that are beneficial to our users, 'nice to haves', that we currently do Issues that are beneficial to our users, 'nice to haves', that we currently do
not have the capacity for or want to give the priority to, are labeled as not have the capacity for or want to give the priority to, are labeled as
...@@ -300,14 +314,14 @@ For guidance on UX implementation at GitLab, please refer to our [Design System] ...@@ -300,14 +314,14 @@ For guidance on UX implementation at GitLab, please refer to our [Design System]
The UX team uses labels to manage their workflow. The UX team uses labels to manage their workflow.
The ~"UX" label on an issue is a signal to the UX team that it will need UX attention. The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook. To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook.
Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue. Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue.
The UX team has a special type label called ~"design artifact". This label indicates that the final output The UX team has a special type label called ~"design artifact". This label indicates that the final output
for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone. for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone.
Any issue labeled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is Any issue labeled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is
needed until the solution has been decided. needed until the solution has been decided.
~"design artifact" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone. ~"design artifact" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone.
......
...@@ -419,7 +419,7 @@ group :ed25519 do ...@@ -419,7 +419,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.101.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.102.0', require: 'gitaly'
gem 'grpc', '~> 1.11.0' gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed # Locked until https://github.com/google/protobuf/issues/4210 is closed
......
...@@ -283,7 +283,7 @@ GEM ...@@ -283,7 +283,7 @@ GEM
gettext_i18n_rails (>= 0.7.1) gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gitaly-proto (0.101.0) gitaly-proto (0.102.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.10) grpc (~> 1.10)
github-linguist (5.3.3) github-linguist (5.3.3)
...@@ -803,7 +803,7 @@ GEM ...@@ -803,7 +803,7 @@ GEM
rubyzip (1.2.1) rubyzip (1.2.1)
rufus-scheduler (3.4.0) rufus-scheduler (3.4.0)
et-orbi (~> 1.0) et-orbi (~> 1.0)
rugged (0.27.1) rugged (0.27.2)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -1039,7 +1039,7 @@ DEPENDENCIES ...@@ -1039,7 +1039,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.101.0) gitaly-proto (~> 0.102.0)
github-linguist (~> 5.3.3) github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2) gitlab-gollum-lib (~> 4.2)
......
...@@ -286,7 +286,7 @@ GEM ...@@ -286,7 +286,7 @@ GEM
gettext_i18n_rails (>= 0.7.1) gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gitaly-proto (0.101.0) gitaly-proto (0.102.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.10) grpc (~> 1.10)
github-linguist (5.3.3) github-linguist (5.3.3)
...@@ -1049,7 +1049,7 @@ DEPENDENCIES ...@@ -1049,7 +1049,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.101.0) gitaly-proto (~> 0.102.0)
github-linguist (~> 5.3.3) github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2) gitlab-gollum-lib (~> 4.2)
......
...@@ -366,7 +366,7 @@ export default class CreateMergeRequestDropdown { ...@@ -366,7 +366,7 @@ export default class CreateMergeRequestDropdown {
removeMessage(target) { removeMessage(target) {
const { input, message } = this.getTargetData(target); const { input, message } = this.getTargetData(target);
const inputClasses = ['gl-field-error-outline', 'gl-field-success-outline']; const inputClasses = ['gl-field-error-outline', 'gl-field-success-outline'];
const messageClasses = ['gl-field-hint', 'gl-field-error-message', 'gl-field-success-message']; const messageClasses = ['text-muted', 'text-danger', 'text-success'];
inputClasses.forEach(cssClass => input.classList.remove(cssClass)); inputClasses.forEach(cssClass => input.classList.remove(cssClass));
messageClasses.forEach(cssClass => message.classList.remove(cssClass)); messageClasses.forEach(cssClass => message.classList.remove(cssClass));
...@@ -393,7 +393,7 @@ export default class CreateMergeRequestDropdown { ...@@ -393,7 +393,7 @@ export default class CreateMergeRequestDropdown {
this.removeMessage(target); this.removeMessage(target);
input.classList.add('gl-field-success-outline'); input.classList.add('gl-field-success-outline');
message.classList.add('gl-field-success-message'); message.classList.add('text-success');
message.textContent = sprintf(__('%{text} is available'), { text }); message.textContent = sprintf(__('%{text} is available'), { text });
message.style.display = 'inline-block'; message.style.display = 'inline-block';
} }
...@@ -403,7 +403,7 @@ export default class CreateMergeRequestDropdown { ...@@ -403,7 +403,7 @@ export default class CreateMergeRequestDropdown {
const text = target === 'branch' ? __('branch name') : __('source'); const text = target === 'branch' ? __('branch name') : __('source');
this.removeMessage(target); this.removeMessage(target);
message.classList.add('gl-field-hint'); message.classList.add('text-muted');
message.textContent = sprintf(__('Checking %{text} availability…'), { text }); message.textContent = sprintf(__('Checking %{text} availability…'), { text });
message.style.display = 'inline-block'; message.style.display = 'inline-block';
} }
...@@ -415,7 +415,7 @@ export default class CreateMergeRequestDropdown { ...@@ -415,7 +415,7 @@ export default class CreateMergeRequestDropdown {
this.removeMessage(target); this.removeMessage(target);
input.classList.add('gl-field-error-outline'); input.classList.add('gl-field-error-outline');
message.classList.add('gl-field-error-message'); message.classList.add('text-danger');
message.textContent = text; message.textContent = text;
message.style.display = 'inline-block'; message.style.display = 'inline-block';
} }
......
...@@ -170,7 +170,6 @@ export default { ...@@ -170,7 +170,6 @@ export default {
:author="author" :author="author"
:created-at="note.created_at" :created-at="note.created_at"
:note-id="note.id" :note-id="note.id"
action-text="commented"
/> />
<note-actions <note-actions
:author-id="author.id" :author-id="author.id"
......
<script> <script>
import { __ } from '~/locale'; import { __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
/** /**
* Port of detail_behavior expand button. * Port of detail_behavior expand button.
* *
...@@ -12,6 +14,9 @@ import { __ } from '~/locale'; ...@@ -12,6 +14,9 @@ import { __ } from '~/locale';
*/ */
export default { export default {
name: 'ExpandButton', name: 'ExpandButton',
components: {
Icon,
},
data() { data() {
return { return {
isCollapsed: true, isCollapsed: true,
...@@ -22,6 +27,9 @@ export default { ...@@ -22,6 +27,9 @@ export default {
return __('Click to expand text'); return __('Click to expand text');
}, },
}, },
destroyed() {
this.isCollapsed = true;
},
methods: { methods: {
onClick() { onClick() {
this.isCollapsed = !this.isCollapsed; this.isCollapsed = !this.isCollapsed;
...@@ -37,7 +45,10 @@ export default { ...@@ -37,7 +45,10 @@ export default {
type="button" type="button"
class="text-expander btn-blank" class="text-expander btn-blank"
@click="onClick"> @click="onClick">
... <icon
:size="12"
name="ellipsis_h"
/>
</button> </button>
<span v-if="!isCollapsed"> <span v-if="!isCollapsed">
<slot name="expanded"></slot> <slot name="expanded"></slot>
......
...@@ -89,6 +89,11 @@ a { ...@@ -89,6 +89,11 @@ a {
color: $gl-link-color; color: $gl-link-color;
} }
a:not(.btn):focus,
a:not(.btn):active {
text-decoration: underline;
}
hr { hr {
overflow: hidden; overflow: hidden;
} }
......
...@@ -193,6 +193,7 @@ ...@@ -193,6 +193,7 @@
&:focus { &:focus {
background: $link-active-background; background: $link-active-background;
color: $gl-text-color; color: $gl-text-color;
text-decoration: none;
} }
} }
......
...@@ -201,6 +201,10 @@ label { ...@@ -201,6 +201,10 @@ label {
} }
.gl-show-field-errors { .gl-show-field-errors {
.form-control {
height: 34px;
}
.gl-field-success-outline { .gl-field-success-outline {
border: 1px solid $green-600; border: 1px solid $green-600;
......
...@@ -135,10 +135,10 @@ ...@@ -135,10 +135,10 @@
} }
.text-expander { .text-expander {
display: inline-block; display: inline-flex;
background: $white-light; background: $white-light;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
padding: 0 4px; padding: 1px $gl-padding-4;
cursor: pointer; cursor: pointer;
border: 1px solid $border-gray-dark; border: 1px solid $border-gray-dark;
border-radius: $border-radius-default; border-radius: $border-radius-default;
...@@ -180,6 +180,11 @@ ...@@ -180,6 +180,11 @@
.commit-content { .commit-content {
padding-right: 10px; padding-right: 10px;
white-space: normal; white-space: normal;
.commit-title {
display: flex;
align-items: center;
}
} }
.commit-actions { .commit-actions {
...@@ -253,7 +258,6 @@ ...@@ -253,7 +258,6 @@
.generic_commit_status { .generic_commit_status {
a, a,
button { button {
color: $gl-text-color;
vertical-align: baseline; vertical-align: baseline;
} }
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
.btn { .btn {
font-size: $gl-font-size; font-size: $gl-font-size;
max-height: 26px;
&[disabled] { &[disabled] {
opacity: 0.3; opacity: 0.3;
......
...@@ -9,7 +9,7 @@ class Admin::HooksController < Admin::ApplicationController ...@@ -9,7 +9,7 @@ class Admin::HooksController < Admin::ApplicationController
end end
def create def create
@hook = SystemHook.new(hook_params) @hook = SystemHook.new(hook_params.to_h)
if @hook.save if @hook.save
redirect_to admin_hooks_path, notice: 'Hook was successfully created.' redirect_to admin_hooks_path, notice: 'Hook was successfully created.'
......
module UploadsActions module UploadsActions
extend ActiveSupport::Concern
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
include SendFileUpload include SendFileUpload
UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo favicon).freeze UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo favicon).freeze
included do
prepend_before_action :set_html_format, only: :show
end
def create def create
link_to_file = UploadService.new(model, params[:file], uploader_class).execute link_to_file = UploadService.new(model, params[:file], uploader_class).execute
...@@ -41,6 +47,13 @@ module UploadsActions ...@@ -41,6 +47,13 @@ module UploadsActions
private private
# Explicitly set the format.
# Otherwise rails 5 will set it from a file extension.
# See https://github.com/rails/rails/commit/84e8accd6fb83031e4c27e44925d7596655285f7#diff-2b8f2fbb113b55ca8e16001c393da8f1
def set_html_format
request.format = :html
end
def uploader_class def uploader_class
raise NotImplementedError raise NotImplementedError
end end
......
...@@ -54,7 +54,7 @@ class DashboardController < Dashboard::ApplicationController ...@@ -54,7 +54,7 @@ class DashboardController < Dashboard::ApplicationController
return unless @no_filters_set return unless @no_filters_set
respond_to do |format| respond_to do |format|
format.html format.html { render }
format.atom { head :bad_request } format.atom { head :bad_request }
end end
end end
......
...@@ -31,7 +31,10 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -31,7 +31,10 @@ class Projects::BranchesController < Projects::ApplicationController
end end
end end
render # https://gitlab.com/gitlab-org/gitlab-ce/issues/48097
Gitlab::GitalyClient.allow_n_plus_1_calls do
render
end
end end
format.json do format.json do
branches = BranchesFinder.new(@repository, params).execute branches = BranchesFinder.new(@repository, params).execute
......
...@@ -247,13 +247,13 @@ class ProjectsController < Projects::ApplicationController ...@@ -247,13 +247,13 @@ class ProjectsController < Projects::ApplicationController
if find_branches if find_branches
branches = BranchesFinder.new(@repository, params).execute.take(100).map(&:name) branches = BranchesFinder.new(@repository, params).execute.take(100).map(&:name)
options[s_('RefSwitcher|Branches')] = branches options['Branches'] = branches
end end
if find_tags && @repository.tag_count.nonzero? if find_tags && @repository.tag_count.nonzero?
tags = TagsFinder.new(@repository, params).execute.take(100).map(&:name) tags = TagsFinder.new(@repository, params).execute.take(100).map(&:name)
options[s_('RefSwitcher|Tags')] = tags options['Tags'] = tags
end end
# If reference is commit id - we should add it to branch/tag selectbox # If reference is commit id - we should add it to branch/tag selectbox
......
...@@ -407,6 +407,7 @@ module ProjectsHelper ...@@ -407,6 +407,7 @@ module ProjectsHelper
@ref || @repository.try(:root_ref) @ref || @repository.try(:root_ref)
end end
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1235
def sanitize_repo_path(project, message) def sanitize_repo_path(project, message)
return '' unless message.present? return '' unless message.present?
......
...@@ -3,7 +3,7 @@ module Clusters ...@@ -3,7 +3,7 @@ module Clusters
class Prometheus < ActiveRecord::Base class Prometheus < ActiveRecord::Base
include PrometheusAdapter include PrometheusAdapter
VERSION = "2.0.0".freeze VERSION = '6.7.3'.freeze
self.table_name = 'clusters_applications_prometheus' self.table_name = 'clusters_applications_prometheus'
...@@ -37,6 +37,7 @@ module Clusters ...@@ -37,6 +37,7 @@ module Clusters
Gitlab::Kubernetes::Helm::InstallCommand.new( Gitlab::Kubernetes::Helm::InstallCommand.new(
name, name,
chart: chart, chart: chart,
version: version,
values: values values: values
) )
end end
......
...@@ -48,7 +48,7 @@ module RedisCacheable ...@@ -48,7 +48,7 @@ module RedisCacheable
def cast_value_from_cache(attribute, value) def cast_value_from_cache(attribute, value)
if Gitlab.rails5? if Gitlab.rails5?
self.class.type_for_attribute(attribute).cast(value) self.class.type_for_attribute(attribute.to_s).cast(value)
else else
self.class.column_for_attribute(attribute).type_cast_from_database(value) self.class.column_for_attribute(attribute).type_cast_from_database(value)
end end
......
...@@ -25,6 +25,7 @@ class Project < ActiveRecord::Base ...@@ -25,6 +25,7 @@ class Project < ActiveRecord::Base
include FastDestroyAll::Helpers include FastDestroyAll::Helpers
include WithUploads include WithUploads
include BatchDestroyDependentAssociations include BatchDestroyDependentAssociations
extend Gitlab::Cache::RequestCache
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
...@@ -2013,6 +2014,11 @@ class Project < ActiveRecord::Base ...@@ -2013,6 +2014,11 @@ class Project < ActiveRecord::Base
@gitlab_deploy_token ||= deploy_tokens.gitlab_deploy_token @gitlab_deploy_token ||= deploy_tokens.gitlab_deploy_token
end end
def any_lfs_file_locks?
lfs_file_locks.any?
end
request_cache(:any_lfs_file_locks?) { self.id }
private private
def storage def storage
......
...@@ -155,6 +155,7 @@ class ChatNotificationService < Service ...@@ -155,6 +155,7 @@ class ChatNotificationService < Service
end end
def notify_for_ref?(data) def notify_for_ref?(data)
return true if data[:object_kind] == 'tag_push'
return true if data.dig(:object_attributes, :tag) return true if data.dig(:object_attributes, :tag)
return true unless notify_only_default_branch? return true unless notify_only_default_branch?
......
...@@ -154,7 +154,10 @@ class Repository ...@@ -154,7 +154,10 @@ class Repository
# Returns a list of commits that are not present in any reference # Returns a list of commits that are not present in any reference
def new_commits(newrev) def new_commits(newrev)
refs = ::Gitlab::Git::RevList.new(raw, newrev: newrev).new_refs # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1233
refs = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
::Gitlab::Git::RevList.new(raw, newrev: newrev).new_refs
end
refs.map { |sha| commit(sha.strip) } refs.map { |sha| commit(sha.strip) }
end end
...@@ -847,7 +850,7 @@ class Repository ...@@ -847,7 +850,7 @@ class Repository
@root_ref_sha ||= commit(root_ref).sha @root_ref_sha ||= commit(root_ref).sha
end end
delegate :merged_branch_names, :can_be_merged?, to: :raw_repository delegate :merged_branch_names, to: :raw_repository
def merge_base(first_commit_id, second_commit_id) def merge_base(first_commit_id, second_commit_id)
first_commit_id = commit(first_commit_id).try(:id) || first_commit_id first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
......
...@@ -297,6 +297,7 @@ class ProjectPolicy < BasePolicy ...@@ -297,6 +297,7 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:build)) prevent(*create_read_update_admin_destroy(:build))
prevent(*create_read_update_admin_destroy(:pipeline_schedule)) prevent(*create_read_update_admin_destroy(:pipeline_schedule))
prevent(*create_read_update_admin_destroy(:environment)) prevent(*create_read_update_admin_destroy(:environment))
prevent(*create_read_update_admin_destroy(:cluster))
prevent(*create_read_update_admin_destroy(:deployment)) prevent(*create_read_update_admin_destroy(:deployment))
end end
......
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
Unfortunately, your email message to GitLab could not be processed. Unfortunately, your email message to GitLab could not be processed.
= markdown @reason = markdown @reason
= render_if_exists 'shared/additional_email_text'
Unfortunately, your email message to GitLab could not be processed. Unfortunately, your email message to GitLab could not be processed.
\ \
= @reason = @reason
= render_if_exists 'shared/additional_email_text'
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
%p %p
Assignee: #{@merge_request.assignee_name} Assignee: #{@merge_request.assignee_name}
= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request
- if @merge_request.description - if @merge_request.description
%div %div
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author) = markdown(@merge_request.description, pipeline: :email, author: @merge_request.author)
...@@ -5,6 +5,6 @@ New Merge Request <%= @merge_request.to_reference %> ...@@ -5,6 +5,6 @@ New Merge Request <%= @merge_request.to_reference %>
<%= merge_path_description(@merge_request, 'to') %> <%= merge_path_description(@merge_request, 'to') %>
Author: <%= @merge_request.author_name %> Author: <%= @merge_request.author_name %>
Assignee: <%= @merge_request.assignee_name %> Assignee: <%= @merge_request.assignee_name %>
<%= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request %>
<%= @merge_request.description %> <%= @merge_request.description %>
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
= view_on_environment_button(@commit.sha, @path, @environment) if @environment = view_on_environment_button(@commit.sha, @path, @environment) if @environment
.btn-group{ role: "group" }< .btn-group{ role: "group" }<
= render_if_exists 'projects/blob/header_file_locks_link'
= edit_blob_button = edit_blob_button
= ide_edit_button = ide_edit_button
- if current_user - if current_user
...@@ -18,3 +19,4 @@ ...@@ -18,3 +19,4 @@
= delete_blob_link = delete_blob_link
= render 'projects/fork_suggestion' = render 'projects/fork_suggestion'
= render_if_exists 'projects/blob/header_file_locks', project: @project, path: @path
...@@ -34,7 +34,8 @@ ...@@ -34,7 +34,8 @@
.d-block.d-sm-none .d-block.d-sm-none
= render_commit_status(commit, ref: ref) = render_commit_status(commit, ref: ref)
- if commit.description? - if commit.description?
%button.text-expander.d-none.d-sm-inline-block.js-toggle-button{ type: "button" } ... %button.text-expander.js-toggle-button
= sprite_icon('ellipsis_h', size: 12)
.commiter .commiter
- commit_author_link = commit_author_link(commit, avatar: false, size: 24) - commit_author_link = commit_author_link(commit, avatar: false, size: 24)
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
%label{ for: 'new-branch-name' } %label{ for: 'new-branch-name' }
= _('Branch name') = _('Branch name')
%input#new-branch-name.js-branch-name.form-control{ type: 'text', placeholder: "#{@issue.to_branch_name}", value: "#{@issue.to_branch_name}" } %input#new-branch-name.js-branch-name.form-control{ type: 'text', placeholder: "#{@issue.to_branch_name}", value: "#{@issue.to_branch_name}" }
%span.js-branch-message.form-text.text-muted %span.js-branch-message.form-text
.form-group .form-group
%label{ for: 'source-name' } %label{ for: 'source-name' }
......
...@@ -69,6 +69,8 @@ ...@@ -69,6 +69,8 @@
.wiki .wiki
= markdown_field(@milestone, :description) = markdown_field(@milestone, :description)
= render_if_exists 'shared/milestones/burndown', milestone: @milestone, project: @project
- if can?(current_user, :read_issue, @project) && @milestone.total_items_count(current_user).zero? - if can?(current_user, :read_issue, @project) && @milestone.total_items_count(current_user).zero?
.alert.alert-success.prepend-top-default .alert.alert-success.prepend-top-default
%span Assign some issues to this milestone. %span Assign some issues to this milestone.
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
= link_to @commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short" = link_to @commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short"
= link_to("#", class: "js-details-expand d-none d-sm-none d-md-inline") do = link_to("#", class: "js-details-expand d-none d-sm-none d-md-inline") do
%span.text-expander %span.text-expander
\... = sprite_icon('ellipsis_h', size: 12)
%span.js-details-content.hide %span.js-details-content.hide
= link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full" = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full"
= clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard") = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
row.find("td.tree-time-ago").html('#{escape_javascript time_ago_with_tooltip(commit.committed_date)}'); row.find("td.tree-time-ago").html('#{escape_javascript time_ago_with_tooltip(commit.committed_date)}');
row.find("td.tree-commit").html('#{escape_javascript render("projects/tree/tree_commit_column", commit: commit)}'); row.find("td.tree-commit").html('#{escape_javascript render("projects/tree/tree_commit_column", commit: commit)}');
= render_if_exists 'projects/refs/logs_tree_lock_label', lock_label: content_data[:lock_label]
- if @more_log_url - if @more_log_url
:plain :plain
if($('#tree-slider').length) { if($('#tree-slider').length) {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
%colgroup %colgroup
%col %col
%col %col
%col.d-none.d-sm-block %col
%col{ width: "120" } %col{ width: "120" }
%thead %thead
%tr %tr
......
...@@ -36,8 +36,6 @@ ...@@ -36,8 +36,6 @@
= note.author.to_reference = note.author.to_reference
%span.note-headline-light %span.note-headline-light
%span.note-headline-meta %span.note-headline-meta
- unless note.system
commented
- if note.system - if note.system
%span.system-note-message %span.system-note-message
= markdown_field(note, :note) = markdown_field(note, :note)
......
...@@ -8,28 +8,12 @@ class RepositoryForkWorker ...@@ -8,28 +8,12 @@ class RepositoryForkWorker
target_project_id = args.shift target_project_id = args.shift
target_project = Project.find(target_project_id) target_project = Project.find(target_project_id)
# By v10.8, we should've drained the queue of all jobs using the old arguments. source_project = target_project.forked_from_project
# We can remove the else clause if we're no longer logging the message in that clause. unless source_project
# See https://gitlab.com/gitlab-org/gitaly/issues/1110 return target_project.mark_import_as_failed('Source project cannot be found.')
if args.empty?
source_project = target_project.forked_from_project
unless source_project
return target_project.mark_import_as_failed('Source project cannot be found.')
end
fork_repository(target_project, source_project.repository_storage, source_project.disk_path)
else
Rails.logger.info("Project #{target_project.id} is being forked using old-style arguments.")
source_repository_storage_path, source_disk_path = *args
source_repository_storage_name = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
Gitlab.config.repositories.storages.find do |_, info|
info.legacy_disk_path == source_repository_storage_path
end&.first || raise("no shard found for path '#{source_repository_storage_path}'")
end
fork_repository(target_project, source_repository_storage_name, source_disk_path)
end end
fork_repository(target_project, source_project.repository_storage, source_project.disk_path)
end end
private private
......
---
title: Fix chat service tag notifications not sending when only default branch enabled
merge_request: 19864
author:
type: fixed
---
title: Omits operartions and kubernetes item from project sidebar when repository or builds are disabled
merge_request: 19835
author:
type: fixed
---
title: Fix branches are not shown in Merge Request dropdown when preferred language
is not English
merge_request: 20016
author: Hiroyuki Sato
type: fixed
---
title: Specify chart version when installing applications on Clusters
merge_request: 20010
author:
type: fixed
---
title: "[Rails5] Fix ActionCable '/cable' mountpoint conflict"
merge_request: 20015
author: "@blackst0ne"
type: fixed
---
title: Add filename filtering to code search
merge_request: 19509
author:
type: added
---
title: Expose whether current user can push into a branch on branches API
merge_request:
author:
type: added
---
title: Serve favicon image always from the main GitLab domain to avoid issues with CORS
merge_request: 19810
author: Alexis Reigel
type: fixed
title: Fixed pagination of groups API
merge_request: 19665
author: Marko, Peter
type: added
---
title: Added id sorting option to GET groups and subgroups API
merge_request: 19665
author: Marko, Peter
type: added
---
title: Rails5 fix format in uploads actions
merge_request: 19907
author: Jasper Maes
type: fixed
---
title: 'Rails5 fix expected: 1 time with arguments: (97, anything, {"squash"=>false})
received: 0 times'
merge_request: 20004
author: Jasper Maes
type: fixed
---
title: 'Rails5 fix expected: 0 times with any arguments received: 1 time with arguments:
DashboardController'
merge_request: 20018
author: Jasper Maes
type: fixed
---
title: Rails5 fix Admin::HooksController
merge_request: 20017
author: Jasper Maes
type: fixed
---
title: Rails5 fix Projects::PagesController spec
merge_request: 20007
author: Jasper Maes
type: fixed
---
title: Bump rugged to 0.27.2
merge_request:
author:
type: fixed
---
title: Eliminate N+1 queries in LFS file locks checks during a push
merge_request:
author:
type: performance
---
title: Updated the icon for expand buttons to ellipsis
merge_request: 18793
author: Constance Okoghenun
type: changed
\ No newline at end of file
---
title: migrate backup rake task to gitaly
merge_request:
author:
type: added
...@@ -57,6 +57,13 @@ module Gitlab ...@@ -57,6 +57,13 @@ module Gitlab
# Configure the default encoding used in templates for Ruby 1.9. # Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8" config.encoding = "utf-8"
# ActionCable mount point.
# The default Rails' mount point is `/cable` which may conflict with existing
# namespaces/users.
# https://github.com/rails/rails/blob/5-0-stable/actioncable/lib/action_cable.rb#L38
# Please change this value when configuring ActionCable for real usage.
config.action_cable.mount_path = "-" if rails5?
# Configure sensitive parameters which will be filtered from the log file. # Configure sensitive parameters which will be filtered from the log file.
# #
# Parameters filtered: # Parameters filtered:
......
...@@ -394,6 +394,7 @@ repositories_storages = Settings.repositories.storages.values ...@@ -394,6 +394,7 @@ repositories_storages = Settings.repositories.storages.values
repository_downloads_path = Settings.gitlab['repository_downloads_path'].to_s.gsub(%r{/$}, '') repository_downloads_path = Settings.gitlab['repository_downloads_path'].to_s.gsub(%r{/$}, '')
repository_downloads_full_path = File.expand_path(repository_downloads_path, Settings.gitlab['user_home']) repository_downloads_full_path = File.expand_path(repository_downloads_path, Settings.gitlab['user_home'])
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1237
Gitlab::GitalyClient::StorageSettings.allow_disk_access do Gitlab::GitalyClient::StorageSettings.allow_disk_access do
if repository_downloads_path.blank? || repositories_storages.any? { |rs| [repository_downloads_path, repository_downloads_full_path].include?(rs.legacy_disk_path.gsub(%r{/$}, '')) } if repository_downloads_path.blank? || repositories_storages.any? { |rs| [repository_downloads_path, repository_downloads_full_path].include?(rs.legacy_disk_path.gsub(%r{/$}, '')) }
Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive')
......
...@@ -37,6 +37,7 @@ def validate_storages_config ...@@ -37,6 +37,7 @@ def validate_storages_config
end end
end end
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1237
def validate_storages_paths def validate_storages_paths
Gitlab::GitalyClient::StorageSettings.allow_disk_access do Gitlab::GitalyClient::StorageSettings.allow_disk_access do
Gitlab.config.repositories.storages.each do |name, repository_storage| Gitlab.config.repositories.storages.each do |name, repository_storage|
......
...@@ -107,7 +107,7 @@ Global Admins GitLab.org/GitLab INT/Global Groups/Global Admins ...@@ -107,7 +107,7 @@ Global Admins GitLab.org/GitLab INT/Global Groups/Global Admins
## GitLab LDAP configuration ## GitLab LDAP configuration
The initial configuration of LDAP in GitLab requires changes to the `gitlab.rb` configuration file. Below is an example of a complete configuration using an Active Directory. The initial configuration of LDAP in GitLab requires changes to the `gitlab.rb` configuration file (`/etc/gitlab/gitlab.rb`). Below is an example of a complete configuration using an Active Directory.
The two Active Directory specific values are `active_directory: true` and `uid: 'sAMAccountName'`. `sAMAccountName` is an attribute returned by Active Directory used for GitLab usernames. See the example output from `ldapsearch` for a full list of attributes a "person" object (user) has in **AD** - [`ldapsearch` example](#using-ldapsearch-unix) The two Active Directory specific values are `active_directory: true` and `uid: 'sAMAccountName'`. `sAMAccountName` is an attribute returned by Active Directory used for GitLab usernames. See the example output from `ldapsearch` for a full list of attributes a "person" object (user) has in **AD** - [`ldapsearch` example](#using-ldapsearch-unix)
......
...@@ -29,6 +29,7 @@ Example response: ...@@ -29,6 +29,7 @@ Example response:
"protected": true, "protected": true,
"developers_can_push": false, "developers_can_push": false,
"developers_can_merge": false, "developers_can_merge": false,
"can_push": true,
"commit": { "commit": {
"author_email": "john@example.com", "author_email": "john@example.com",
"author_name": "John Smith", "author_name": "John Smith",
...@@ -76,6 +77,7 @@ Example response: ...@@ -76,6 +77,7 @@ Example response:
"protected": true, "protected": true,
"developers_can_push": false, "developers_can_push": false,
"developers_can_merge": false, "developers_can_merge": false,
"can_push": true,
"commit": { "commit": {
"author_email": "john@example.com", "author_email": "john@example.com",
"author_name": "John Smith", "author_name": "John Smith",
...@@ -140,7 +142,8 @@ Example response: ...@@ -140,7 +142,8 @@ Example response:
"merged": false, "merged": false,
"protected": true, "protected": true,
"developers_can_push": true, "developers_can_push": true,
"developers_can_merge": true "developers_can_merge": true,
"can_push": true
} }
``` ```
...@@ -188,7 +191,8 @@ Example response: ...@@ -188,7 +191,8 @@ Example response:
"merged": false, "merged": false,
"protected": false, "protected": false,
"developers_can_push": false, "developers_can_push": false,
"developers_can_merge": false "developers_can_merge": false,
"can_push": true
} }
``` ```
...@@ -231,7 +235,8 @@ Example response: ...@@ -231,7 +235,8 @@ Example response:
"merged": false, "merged": false,
"protected": false, "protected": false,
"developers_can_push": false, "developers_can_push": false,
"developers_can_merge": false "developers_can_merge": false,
"can_push": true
} }
``` ```
......
...@@ -12,7 +12,7 @@ Parameters: ...@@ -12,7 +12,7 @@ Parameters:
| `skip_groups` | array of integers | no | Skip the group IDs passed | | `skip_groups` | array of integers | no | Skip the group IDs passed |
| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) | | `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
| `search` | string | no | Return the list of authorized groups matching the search criteria | | `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` | | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) | | `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
...@@ -96,7 +96,7 @@ Parameters: ...@@ -96,7 +96,7 @@ Parameters:
| `skip_groups` | array of integers | no | Skip the group IDs passed | | `skip_groups` | array of integers | no | Skip the group IDs passed |
| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) | | `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
| `search` | string | no | Return the list of authorized groups matching the search criteria | | `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` | | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) | | `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
......
...@@ -173,3 +173,7 @@ DELETE /projects/:id/members/:user_id ...@@ -173,3 +173,7 @@ DELETE /projects/:id/members/:user_id
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/members/:user_id curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/members/:user_id
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/members/:user_id curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/members/:user_id
``` ```
## Give a group access to a project
Look at [share project with group](projects.md#share-project-with-group)
...@@ -122,11 +122,11 @@ POST /projects/:id/pages/domains ...@@ -122,11 +122,11 @@ POST /projects/:id/pages/domains
| `key` | file/string | no | The certificate key in PEM format. | | `key` | file/string | no | The certificate key in PEM format. |
```bash ```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form="domain=ssl.domain.example" --form="certificate=@/path/to/cert.pem" --form="key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "domain=ssl.domain.example" --form "certificate=@/path/to/cert.pem" --form "key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains
``` ```
```bash ```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form="domain=ssl.domain.example" --form="certificate=$CERT_PEM" --form="key=$KEY_PEM" https://gitlab.example.com/api/v4/projects/5/pages/domains curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "domain=ssl.domain.example" --form "certificate=$CERT_PEM" --form "key=$KEY_PEM" https://gitlab.example.com/api/v4/projects/5/pages/domains
``` ```
```json ```json
...@@ -158,11 +158,11 @@ PUT /projects/:id/pages/domains/:domain ...@@ -158,11 +158,11 @@ PUT /projects/:id/pages/domains/:domain
| `key` | file/string | no | The certificate key in PEM format. | | `key` | file/string | no | The certificate key in PEM format. |
```bash ```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form="certificate=@/path/to/cert.pem" --form="key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "certificate=@/path/to/cert.pem" --form "key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
``` ```
```bash ```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form="certificate=$CERT_PEM" --form="key=$KEY_PEM" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "certificate=$CERT_PEM" --form "key=$KEY_PEM" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
``` ```
```json ```json
......
...@@ -1198,7 +1198,7 @@ POST /projects/:id/share ...@@ -1198,7 +1198,7 @@ POST /projects/:id/share
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `group_id` | integer | yes | The ID of the group to share with | | `group_id` | integer | yes | The ID of the group to share with |
| `group_access` | integer | yes | The permissions level to grant the group | | `group_access` | integer | yes | The [permissions level](members.md) to grant the group |
| `expires_at` | string | no | Share expiration date in ISO 8601 format: 2016-09-26 | | `expires_at` | string | no | Share expiration date in ISO 8601 format: 2016-09-26 |
## Delete a shared project link within a group ## Delete a shared project link within a group
......
...@@ -776,6 +776,15 @@ Example response: ...@@ -776,6 +776,15 @@ Example response:
### Scope: blobs ### Scope: blobs
Filters are available for this scope:
- filename
- path
- extension
to use a filter simply include it in your query like so: `a query filename:some_name*`.
You may use wildcards (`*`) to use glob matching.
```bash ```bash
curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/6/search?scope=blobs&search=installation curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/6/search?scope=blobs&search=installation
``` ```
......
...@@ -88,18 +88,18 @@ The example below simply moves all files from the root of the project to the ...@@ -88,18 +88,18 @@ The example below simply moves all files from the root of the project to the
`public/` directory. The `.public` workaround is so `cp` doesn't also copy `public/` directory. The `.public` workaround is so `cp` doesn't also copy
`public/` to itself in an infinite loop: `public/` to itself in an infinite loop:
``` ```yaml
pages: pages:
stage: deploy stage: deploy
script: script:
- mkdir .public - mkdir .public
- cp -r * .public - cp -r * .public
- mv .public public - mv .public public
artifacts: artifacts:
paths: paths:
- public - public
only: only:
- master - master
``` ```
Read more on [GitLab Pages user documentation](../../user/project/pages/index.md). Read more on [GitLab Pages user documentation](../../user/project/pages/index.md).
...@@ -131,15 +131,15 @@ if you set it per-job: ...@@ -131,15 +131,15 @@ if you set it per-job:
```yaml ```yaml
before_script: before_script:
- global before script - global before script
job: job:
before_script: before_script:
- execute this instead of global before script - execute this instead of global before script
script: script:
- my command - my command
after_script: after_script:
- execute this after my script - execute this after my script
``` ```
## `stages` ## `stages`
...@@ -409,18 +409,18 @@ fails, it will not stop the next stage from running, since it's marked with ...@@ -409,18 +409,18 @@ fails, it will not stop the next stage from running, since it's marked with
job1: job1:
stage: test stage: test
script: script:
- execute_script_that_will_fail - execute_script_that_will_fail
allow_failure: true allow_failure: true
job2: job2:
stage: test stage: test
script: script:
- execute_script_that_will_succeed - execute_script_that_will_succeed
job3: job3:
stage: deploy stage: deploy
script: script:
- deploy_to_staging - deploy_to_staging
``` ```
## `when` ## `when`
...@@ -442,38 +442,38 @@ For example: ...@@ -442,38 +442,38 @@ For example:
```yaml ```yaml
stages: stages:
- build - build
- cleanup_build - cleanup_build
- test - test
- deploy - deploy
- cleanup - cleanup
build_job: build_job:
stage: build stage: build
script: script:
- make build - make build
cleanup_build_job: cleanup_build_job:
stage: cleanup_build stage: cleanup_build
script: script:
- cleanup build when failed - cleanup build when failed
when: on_failure when: on_failure
test_job: test_job:
stage: test stage: test
script: script:
- make test - make test
deploy_job: deploy_job:
stage: deploy stage: deploy
script: script:
- make deploy - make deploy
when: manual when: manual
cleanup_job: cleanup_job:
stage: cleanup stage: cleanup
script: script:
- cleanup after jobs - cleanup after jobs
when: always when: always
``` ```
...@@ -734,8 +734,8 @@ rspec: ...@@ -734,8 +734,8 @@ rspec:
script: test script: test
cache: cache:
paths: paths:
- binaries/*.apk - binaries/*.apk
- .config - .config
``` ```
Locally defined cache overrides globally defined options. The following `rspec` Locally defined cache overrides globally defined options. The following `rspec`
...@@ -744,14 +744,14 @@ job will cache only `binaries/`: ...@@ -744,14 +744,14 @@ job will cache only `binaries/`:
```yaml ```yaml
cache: cache:
paths: paths:
- my/files - my/files
rspec: rspec:
script: test script: test
cache: cache:
key: rspec key: rspec
paths: paths:
- binaries/ - binaries/
``` ```
Note that since cache is shared between jobs, if you're using different Note that since cache is shared between jobs, if you're using different
...@@ -786,7 +786,7 @@ For example, to enable per-branch caching: ...@@ -786,7 +786,7 @@ For example, to enable per-branch caching:
cache: cache:
key: "$CI_COMMIT_REF_SLUG" key: "$CI_COMMIT_REF_SLUG"
paths: paths:
- binaries/ - binaries/
``` ```
If you use **Windows Batch** to run your shell scripts you need to replace If you use **Windows Batch** to run your shell scripts you need to replace
...@@ -796,7 +796,7 @@ If you use **Windows Batch** to run your shell scripts you need to replace ...@@ -796,7 +796,7 @@ If you use **Windows Batch** to run your shell scripts you need to replace
cache: cache:
key: "%CI_COMMIT_REF_SLUG%" key: "%CI_COMMIT_REF_SLUG%"
paths: paths:
- binaries/ - binaries/
``` ```
### `cache:untracked` ### `cache:untracked`
...@@ -819,7 +819,7 @@ rspec: ...@@ -819,7 +819,7 @@ rspec:
cache: cache:
untracked: true untracked: true
paths: paths:
- binaries/ - binaries/
``` ```
### `cache:policy` ### `cache:policy`
...@@ -897,8 +897,8 @@ Send all files in `binaries` and `.config`: ...@@ -897,8 +897,8 @@ Send all files in `binaries` and `.config`:
```yaml ```yaml
artifacts: artifacts:
paths: paths:
- binaries/ - binaries/
- .config - .config
``` ```
To disable artifact passing, define the job with empty [dependencies](#dependencies): To disable artifact passing, define the job with empty [dependencies](#dependencies):
...@@ -927,7 +927,7 @@ release-job: ...@@ -927,7 +927,7 @@ release-job:
- mvn package -U - mvn package -U
artifacts: artifacts:
paths: paths:
- target/*.war - target/*.war
only: only:
- tags - tags
``` ```
...@@ -949,7 +949,7 @@ job: ...@@ -949,7 +949,7 @@ job:
artifacts: artifacts:
name: "$CI_JOB_NAME" name: "$CI_JOB_NAME"
paths: paths:
- binaries/ - binaries/
``` ```
To create an archive with a name of the current branch or tag including only To create an archive with a name of the current branch or tag including only
...@@ -960,7 +960,7 @@ job: ...@@ -960,7 +960,7 @@ job:
artifacts: artifacts:
name: "$CI_COMMIT_REF_NAME" name: "$CI_COMMIT_REF_NAME"
paths: paths:
- binaries/ - binaries/
``` ```
To create an archive with a name of the current job and the current branch or To create an archive with a name of the current job and the current branch or
...@@ -971,7 +971,7 @@ job: ...@@ -971,7 +971,7 @@ job:
artifacts: artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME" name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
paths: paths:
- binaries/ - binaries/
``` ```
To create an archive with a name of the current [stage](#stages) and branch name: To create an archive with a name of the current [stage](#stages) and branch name:
...@@ -981,7 +981,7 @@ job: ...@@ -981,7 +981,7 @@ job:
artifacts: artifacts:
name: "$CI_JOB_STAGE-$CI_COMMIT_REF_NAME" name: "$CI_JOB_STAGE-$CI_COMMIT_REF_NAME"
paths: paths:
- binaries/ - binaries/
``` ```
--- ---
...@@ -994,7 +994,7 @@ job: ...@@ -994,7 +994,7 @@ job:
artifacts: artifacts:
name: "%CI_JOB_STAGE%-%CI_COMMIT_REF_NAME%" name: "%CI_JOB_STAGE%-%CI_COMMIT_REF_NAME%"
paths: paths:
- binaries/ - binaries/
``` ```
If you use **Windows PowerShell** to run your shell scripts you need to replace If you use **Windows PowerShell** to run your shell scripts you need to replace
...@@ -1005,7 +1005,7 @@ job: ...@@ -1005,7 +1005,7 @@ job:
artifacts: artifacts:
name: "$env:CI_JOB_STAGE-$env:CI_COMMIT_REF_NAME" name: "$env:CI_JOB_STAGE-$env:CI_COMMIT_REF_NAME"
paths: paths:
- binaries/ - binaries/
``` ```
### `artifacts:untracked` ### `artifacts:untracked`
...@@ -1030,7 +1030,7 @@ Send all Git untracked files and files in `binaries`: ...@@ -1030,7 +1030,7 @@ Send all Git untracked files and files in `binaries`:
artifacts: artifacts:
untracked: true untracked: true
paths: paths:
- binaries/ - binaries/
``` ```
### `artifacts:when` ### `artifacts:when`
...@@ -1120,26 +1120,26 @@ build:osx: ...@@ -1120,26 +1120,26 @@ build:osx:
script: make build:osx script: make build:osx
artifacts: artifacts:
paths: paths:
- binaries/ - binaries/
build:linux: build:linux:
stage: build stage: build
script: make build:linux script: make build:linux
artifacts: artifacts:
paths: paths:
- binaries/ - binaries/
test:osx: test:osx:
stage: test stage: test
script: make test:osx script: make test:osx
dependencies: dependencies:
- build:osx - build:osx
test:linux: test:linux:
stage: test stage: test
script: make test:linux script: make test:linux
dependencies: dependencies:
- build:linux - build:linux
deploy: deploy:
stage: deploy stage: deploy
......
...@@ -252,6 +252,53 @@ Keep in mind that the relation passed to ...@@ -252,6 +252,53 @@ Keep in mind that the relation passed to
`change_column_type_using_background_migration` _must_ include `EachBatch`, `change_column_type_using_background_migration` _must_ include `EachBatch`,
otherwise it will raise a `TypeError`. otherwise it will raise a `TypeError`.
This migration then needs to be followed in a separate release (_not_ a patch
release) by a cleanup migration, which should steal from the queue and handle
any remaining rows. For example:
```ruby
class MigrateRemainingIssuesClosedAt < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
class Issue < ActiveRecord::Base
self.table_name = 'issues'
include EachBatch
end
def up
Gitlab::BackgroundMigration.steal('CopyColumn')
Gitlab::BackgroundMigration.steal('CleanupConcurrentTypeChange')
migrate_remaining_rows if migrate_column_type?
end
def down
# Previous migrations already revert the changes made here.
end
def migrate_remaining_rows
Issue.where('closed_at_for_type_change IS NULL AND closed_at IS NOT NULL').each_batch do |batch|
batch.update_all('closed_at_for_type_change = closed_at')
end
cleanup_concurrent_column_type_change(:issues, :closed_at)
end
def migrate_column_type?
# Some environments may have already executed the previous version of this
# migration, thus we don't need to migrate those environments again.
column_for('issues', 'closed_at').type == :datetime # rubocop:disable Migration/Datetime
end
end
```
For more information, see [the documentation on cleaning up background
migrations](background_migrations.md#cleaning-up).
## Adding Indexes ## Adding Indexes
Adding indexes is an expensive process that blocks INSERT and UPDATE queries for Adding indexes is an expensive process that blocks INSERT and UPDATE queries for
......
...@@ -154,12 +154,12 @@ page](https://golang.org/dl). ...@@ -154,12 +154,12 @@ page](https://golang.org/dl).
# Remove former Go installation folder # Remove former Go installation folder
sudo rm -rf /usr/local/go sudo rm -rf /usr/local/go
curl --remote-name --progress https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
echo '1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz' | shasum -a256 -c - && \ echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
rm go1.8.3.linux-amd64.tar.gz rm go1.10.3.linux-amd64.tar.gz
## 4. Node ## 4. Node
......
...@@ -168,7 +168,7 @@ want their accounts to be upgraded to full internal accounts. ...@@ -168,7 +168,7 @@ want their accounts to be upgraded to full internal accounts.
>**Note:** >**Note:**
The following information only applies for installations from source. The following information only applies for installations from source.
GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships GitLab uses [Omniauth](https://github.com/omniauth/omniauth) for authentication and already ships
with a few providers pre-installed (e.g. LDAP, GitHub, Twitter). But sometimes that with a few providers pre-installed (e.g. LDAP, GitHub, Twitter). But sometimes that
is not enough and you need to integrate with other authentication solutions. For is not enough and you need to integrate with other authentication solutions. For
these cases you can use the Omniauth provider. these cases you can use the Omniauth provider.
......
...@@ -81,8 +81,8 @@ More information can be found on the [yarn website](https://yarnpkg.com/en/docs/ ...@@ -81,8 +81,8 @@ More information can be found on the [yarn website](https://yarnpkg.com/en/docs/
### 5. Update Go ### 5. Update Go
NOTE: GitLab 9.2 and higher only supports Go 1.8.3 and dropped support for Go NOTE: GitLab 11.0 and higher only supports Go 1.9.x and newer, and dropped support for Go
1.5.x through 1.7.x. Be sure to upgrade your installation if necessary. 1.5.x through 1.8.x. Be sure to upgrade your installation if necessary.
You can check which version you are running with `go version`. You can check which version you are running with `go version`.
...@@ -92,11 +92,11 @@ Download and install Go: ...@@ -92,11 +92,11 @@ Download and install Go:
# Remove former Go installation folder # Remove former Go installation folder
sudo rm -rf /usr/local/go sudo rm -rf /usr/local/go
curl --remote-name --progress https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
echo '1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz' | shasum -a256 -c - && \ echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/ sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
rm go1.8.3.linux-amd64.tar.gz rm go1.10.3.linux-amd64.tar.gz
``` ```
### 6. Get latest code ### 6. Get latest code
......
...@@ -25,6 +25,9 @@ See our [product handbook on permissions](https://about.gitlab.com/handbook/prod ...@@ -25,6 +25,9 @@ See our [product handbook on permissions](https://about.gitlab.com/handbook/prod
## Project members permissions ## Project members permissions
NOTE: **Note:**
In GitLab 11.0, the Master role was renamed to Maintainer.
The following table depicts the various user permission levels in a project. The following table depicts the various user permission levels in a project.
| Action | Guest | Reporter | Developer |Maintainer| Owner | | Action | Guest | Reporter | Developer |Maintainer| Owner |
...@@ -146,6 +149,9 @@ read through the documentation on [permissions and access to confidential issues ...@@ -146,6 +149,9 @@ read through the documentation on [permissions and access to confidential issues
## Group members permissions ## Group members permissions
NOTE: **Note:**
In GitLab 11.0, the Master role was renamed to Maintainer.
Any user can remove themselves from a group, unless they are the last Owner of Any user can remove themselves from a group, unless they are the last Owner of
the group. The following table depicts the various user permission levels in a the group. The following table depicts the various user permission levels in a
group. group.
...@@ -223,6 +229,9 @@ which visibility level you select on project settings. ...@@ -223,6 +229,9 @@ which visibility level you select on project settings.
## GitLab CI/CD permissions ## GitLab CI/CD permissions
NOTE: **Note:**
In GitLab 11.0, the Master role was renamed to Maintainer.
GitLab CI/CD permissions rely on the role the user has in GitLab. There are four GitLab CI/CD permissions rely on the role the user has in GitLab. There are four
permission levels in total: permission levels in total:
...@@ -250,6 +259,9 @@ instance and project. In addition, all admins can use the admin interface under ...@@ -250,6 +259,9 @@ instance and project. In addition, all admins can use the admin interface under
### Job permissions ### Job permissions
NOTE: **Note:**
In GitLab 11.0, the Master role was renamed to Maintainer.
>**Note:** >**Note:**
GitLab 8.12 has a completely redesigned job permissions system. GitLab 8.12 has a completely redesigned job permissions system.
Read all about the [new model and its implications][new-mod]. Read all about the [new model and its implications][new-mod].
...@@ -301,4 +313,4 @@ Read through the documentation on [LDAP users permissions](https://docs.gitlab.c ...@@ -301,4 +313,4 @@ Read through the documentation on [LDAP users permissions](https://docs.gitlab.c
[ce-18994]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18994 [ce-18994]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18994
[new-mod]: project/new_ci_build_permissions_model.md [new-mod]: project/new_ci_build_permissions_model.md
[ee-998]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998 [ee-998]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998
[eep]: https://about.gitlab.com/products/ [eep]: https://about.gitlab.com/products/
\ No newline at end of file
...@@ -4,19 +4,14 @@ A user's profile preferences page allows the user to customize various aspects ...@@ -4,19 +4,14 @@ A user's profile preferences page allows the user to customize various aspects
of GitLab to their liking. of GitLab to their liking.
To navigate to your profile's preferences, click your avatar icon in the top To navigate to your profile's preferences, click your avatar icon in the top
right corner and select **Settings**. From there on, choose the **Preferences** right corner, select **Settings** and then choose **Preferences** from the
tab. left sidebar.
![Profile preferences settings](img/profile_settings_dropdown.png)
## Navigation theme ## Navigation theme
>**Note:**
Navigation themes have been re-introduced with [GitLab 10.0](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/).
The GitLab navigation theme setting allows you to personalize your GitLab experience. The GitLab navigation theme setting allows you to personalize your GitLab experience.
You can choose from several color themes that add unique colors to the top navigation You can choose from several color themes that add unique colors to the top navigation
and left side navigation. and left side navigation.
Using individual color themes might help you differentiate between your different Using individual color themes might help you differentiate between your different
GitLab instances. GitLab instances.
...@@ -33,13 +28,13 @@ The default palette is Indigo. You can choose between 10 different themes: ...@@ -33,13 +28,13 @@ The default palette is Indigo. You can choose between 10 different themes:
- Dark - Dark
- Light - Light
![Profile preferences syntax highlighting themes](img/profile-preferences-syntax-themes.png) ![Profile preferences navigation themes](img/profil-preferences-navigation-theme.png)
## Syntax highlighting theme ## Syntax highlighting theme
>**Note:** NOTE: **Note:**
GitLab uses the [rouge Ruby library][rouge] for syntax highlighting. For a GitLab uses the [rouge Ruby library](http://rouge.jneen.net/ "Rouge website")
list of supported languages visit the rouge website. for syntax highlighting. For a list of supported languages visit the rouge website.
Changing this setting allows you to customize the color theme when viewing any Changing this setting allows you to customize the color theme when viewing any
syntax highlighted code on GitLab. syntax highlighted code on GitLab.
...@@ -52,7 +47,7 @@ The default syntax theme is White, and you can choose among 5 different colors: ...@@ -52,7 +47,7 @@ The default syntax theme is White, and you can choose among 5 different colors:
- Solarized dark - Solarized dark
- Monokai - Monokai
![Profile preferences navigation themes](img/profil-preferences-navigation-theme.png) ![Profile preferences syntax highlighting themes](img/profile-preferences-syntax-themes.png)
## Behavior ## Behavior
...@@ -78,7 +73,7 @@ You have 8 options here that you can use for your default dashboard view: ...@@ -78,7 +73,7 @@ You have 8 options here that you can use for your default dashboard view:
- Your projects' activity - Your projects' activity
- Starred projects' activity - Starred projects' activity
- Your groups - Your groups
- Your [Todos] - Your [Todos](../../workflow/todos.md)
- Assigned Issues - Assigned Issues
- Assigned Merge Requests - Assigned Merge Requests
...@@ -92,6 +87,3 @@ You can choose between 3 options: ...@@ -92,6 +87,3 @@ You can choose between 3 options:
- Files and Readme (default) - Files and Readme (default)
- Readme - Readme
- Activity - Activity
[rouge]: http://rouge.jneen.net/ "Rouge website"
[todos]: ../../workflow/todos.md
...@@ -156,7 +156,7 @@ added directly to your configured cluster. Those applications are needed for ...@@ -156,7 +156,7 @@ added directly to your configured cluster. Those applications are needed for
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. | | [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. |
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications | | [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications |
| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | | [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. |
| [JupyterHub](http://jupyter.org/) | 11.0+ | The Jupyter Notebook is an open-source web application that allows you to create and share documents that contain live code, equations, visualizations and narrative text. | | [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. |
## Getting the external IP address ## Getting the external IP address
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
## On Microsoft Teams ## On Microsoft Teams
To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft Teams by following the steps described in this [document](https://msdn.microsoft.com/en-us/microsoft-teams/connectors). To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft Teams by following the steps described in this [document](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors#setting-up-a-custom-incoming-webhook).
## On GitLab ## On GitLab
......
...@@ -176,4 +176,12 @@ Lock your files to prevent any conflicting changes. ...@@ -176,4 +176,12 @@ Lock your files to prevent any conflicting changes.
You can access your repos via [repository API](../../../api/repositories.md). You can access your repos via [repository API](../../../api/repositories.md).
## Clone in Apple Xcode
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/45820) in GitLab 11.0
Projects that contain a `.xcodeproj` or `.xcworkspace` directory can now be cloned
in Xcode using the new **Open in Xcode** button, located next to the Git URL
used for cloning your project. The button is only shown on macOS.
[jupyter]: https://jupyter.org [jupyter]: https://jupyter.org
...@@ -349,6 +349,10 @@ module API ...@@ -349,6 +349,10 @@ module API
expose :developers_can_merge do |repo_branch, options| expose :developers_can_merge do |repo_branch, options|
options[:project].protected_branches.developers_can?(:merge, repo_branch.name) options[:project].protected_branches.developers_can?(:merge, repo_branch.name)
end end
expose :can_push do |repo_branch, options|
Gitlab::UserAccess.new(options[:current_user], project: options[:project]).can_push_to_branch?(repo_branch.name)
end
end end
class TreeObject < Grape::Entity class TreeObject < Grape::Entity
......
...@@ -32,7 +32,7 @@ module API ...@@ -32,7 +32,7 @@ module API
optional :all_available, type: Boolean, desc: 'Show all group that you have access to' optional :all_available, type: Boolean, desc: 'Show all group that you have access to'
optional :search, type: String, desc: 'Search for a specific group' optional :search, type: String, desc: 'Search for a specific group'
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
optional :order_by, type: String, values: %w[name path], default: 'name', desc: 'Order by name or path' optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)' optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
use :pagination use :pagination
end end
...@@ -46,7 +46,9 @@ module API ...@@ -46,7 +46,9 @@ module API
groups = GroupsFinder.new(current_user, find_params).execute groups = GroupsFinder.new(current_user, find_params).execute
groups = groups.search(params[:search]) if params[:search].present? groups = groups.search(params[:search]) if params[:search].present?
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present? groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
groups = groups.reorder(params[:order_by] => params[:sort]) order_options = { params[:order_by] => params[:sort] }
order_options["id"] ||= "asc"
groups = groups.reorder(order_options)
groups groups
end end
......
...@@ -4,7 +4,6 @@ require_relative 'helper' ...@@ -4,7 +4,6 @@ require_relative 'helper'
module Backup module Backup
class Repository class Repository
include Backup::Helper include Backup::Helper
# rubocop:disable Metrics/AbcSize
attr_reader :progress attr_reader :progress
...@@ -18,61 +17,26 @@ module Backup ...@@ -18,61 +17,26 @@ module Backup
Project.find_each(batch_size: 1000) do |project| Project.find_each(batch_size: 1000) do |project|
progress.print " * #{display_repo_path(project)} ... " progress.print " * #{display_repo_path(project)} ... "
path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
path_to_repo(project)
end
path_to_project_bundle = path_to_bundle(project)
# Create namespace dir or hashed path if missing
if project.hashed_storage?(:repository) if project.hashed_storage?(:repository)
FileUtils.mkdir_p(File.dirname(File.join(backup_repos_path, project.disk_path))) FileUtils.mkdir_p(File.dirname(File.join(backup_repos_path, project.disk_path)))
else else
FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.full_path)) if project.namespace FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.full_path)) if project.namespace
end end
if empty_repo?(project) if !empty_repo?(project)
progress.puts "[SKIPPED]".color(:cyan) backup_project(project)
progress.puts "[DONE]".color(:green)
else else
in_path(path_to_project_repo) do |dir| progress.puts "[SKIPPED]".color(:cyan)
FileUtils.mkdir_p(path_to_tars(project))
cmd = %W(tar -cf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir})
output, status = Gitlab::Popen.popen(cmd)
unless status.zero?
progress_warn(project, cmd.join(' '), output)
end
end
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path_to_project_repo} bundle create #{path_to_project_bundle} --all)
output, status = Gitlab::Popen.popen(cmd)
if status.zero?
progress.puts "[DONE]".color(:green)
else
progress_warn(project, cmd.join(' '), output)
end
end end
wiki = ProjectWiki.new(project) wiki = ProjectWiki.new(project)
path_to_wiki_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
path_to_repo(wiki)
end
path_to_wiki_bundle = path_to_bundle(wiki)
if File.exist?(path_to_wiki_repo) if !empty_repo?(wiki)
progress.print " * #{display_repo_path(wiki)} ... " backup_project(wiki)
progress.puts "[DONE] Wiki".color(:green)
if empty_repo?(wiki) else
progress.puts " [SKIPPED]".color(:cyan) progress.puts "[SKIPPED] Wiki".color(:cyan)
else
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path_to_wiki_repo} bundle create #{path_to_wiki_bundle} --all)
output, status = Gitlab::Popen.popen(cmd)
if status.zero?
progress.puts " [DONE]".color(:green)
else
progress_warn(wiki, cmd.join(' '), output)
end
end
end end
end end
end end
...@@ -83,6 +47,38 @@ module Backup ...@@ -83,6 +47,38 @@ module Backup
end end
end end
def backup_project(project)
gitaly_migrate(:repository_backup) do |is_enabled|
if is_enabled
backup_project_gitaly(project)
else
backup_project_local(project)
end
end
backup_custom_hooks(project)
rescue => e
progress_warn(project, e, 'Failed to backup repo')
end
def backup_project_gitaly(project)
path_to_project_bundle = path_to_bundle(project)
Gitlab::GitalyClient::RepositoryService.new(project.repository)
.create_bundle(path_to_project_bundle)
end
def backup_project_local(project)
path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
path_to_repo(project)
end
path_to_project_bundle = path_to_bundle(project)
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path_to_project_repo} bundle create #{path_to_project_bundle} --all)
output, status = Gitlab::Popen.popen(cmd)
progress_warn(project, cmd.join(' '), output) unless status.zero?
end
def delete_all_repositories(name, repository_storage) def delete_all_repositories(name, repository_storage)
gitaly_migrate(:delete_all_repositories) do |is_enabled| gitaly_migrate(:delete_all_repositories) do |is_enabled|
if is_enabled if is_enabled
...@@ -97,8 +93,6 @@ module Backup ...@@ -97,8 +93,6 @@ module Backup
path = repository_storage.legacy_disk_path path = repository_storage.legacy_disk_path
return unless File.exist?(path) return unless File.exist?(path)
# Move all files in the existing repos directory except . and .. to
# repositories.old.<timestamp> directory
bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s) bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s)
FileUtils.mkdir_p(bk_repos_path, mode: 0700) FileUtils.mkdir_p(bk_repos_path, mode: 0700)
files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")] files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")]
...@@ -129,13 +123,47 @@ module Backup ...@@ -129,13 +123,47 @@ module Backup
.restore_custom_hooks(custom_hooks_path) .restore_custom_hooks(custom_hooks_path)
end end
def local_backup_custom_hooks(project)
in_path(path_to_tars(project)) do |dir|
path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
path_to_repo(project)
end
break unless File.exist?(File.join(path_to_project_repo, dir))
FileUtils.mkdir_p(path_to_tars(project))
cmd = %W(tar -cf #{path_to_tars(project, dir)} -c #{path_to_project_repo} #{dir})
output, status = Gitlab::Popen.popen(cmd)
unless status.zero?
progress_warn(project, cmd.join(' '), output)
end
end
end
def gitaly_backup_custom_hooks(project)
FileUtils.mkdir_p(path_to_tars(project))
custom_hooks_path = path_to_tars(project, 'custom_hooks')
Gitlab::GitalyClient::RepositoryService.new(project.repository)
.backup_custom_hooks(custom_hooks_path)
end
def backup_custom_hooks(project)
gitaly_migrate(:backup_custom_hooks) do |is_enabled|
if is_enabled
gitaly_backup_custom_hooks(project)
else
local_backup_custom_hooks(project)
end
end
end
def restore_custom_hooks(project) def restore_custom_hooks(project)
in_path(path_to_tars(project)) do |dir| in_path(path_to_tars(project)) do |dir|
gitaly_migrate(:restore_custom_hooks) do |is_enabled| gitaly_migrate(:restore_custom_hooks) do |is_enabled|
if is_enabled if is_enabled
local_restore_custom_hooks(project, dir)
else
gitaly_restore_custom_hooks(project, dir) gitaly_restore_custom_hooks(project, dir)
else
local_restore_custom_hooks(project, dir)
end end
end end
end end
...@@ -186,7 +214,6 @@ module Backup ...@@ -186,7 +214,6 @@ module Backup
end end
end end
end end
# rubocop:enable Metrics/AbcSize
protected protected
...@@ -224,9 +251,7 @@ module Backup ...@@ -224,9 +251,7 @@ module Backup
def prepare def prepare
FileUtils.rm_rf(backup_repos_path) FileUtils.rm_rf(backup_repos_path)
# Ensure the parent dir of backup_repos_path exists
FileUtils.mkdir_p(Gitlab.config.backup.path) FileUtils.mkdir_p(Gitlab.config.backup.path)
# Fail if somebody raced to create backup_repos_path before us
FileUtils.mkdir(backup_repos_path, mode: 0700) FileUtils.mkdir(backup_repos_path, mode: 0700)
end end
...@@ -242,7 +267,6 @@ module Backup ...@@ -242,7 +267,6 @@ module Backup
end end
def empty_repo?(project_or_wiki) def empty_repo?(project_or_wiki)
# Protect against stale caches
project_or_wiki.repository.expire_emptiness_caches project_or_wiki.repository.expire_emptiness_caches
project_or_wiki.repository.empty? project_or_wiki.repository.empty?
end end
......
...@@ -37,7 +37,7 @@ module Gitlab ...@@ -37,7 +37,7 @@ module Gitlab
def validate_lfs_file_locks? def validate_lfs_file_locks?
strong_memoize(:validate_lfs_file_locks) do strong_memoize(:validate_lfs_file_locks) do
project.lfs_enabled? && project.lfs_file_locks.any? && newrev && oldrev project.lfs_enabled? && newrev && oldrev && project.any_lfs_file_locks?
end end
end end
......
...@@ -7,18 +7,10 @@ module Gitlab ...@@ -7,18 +7,10 @@ module Gitlab
# Created or deleted branch # Created or deleted branch
return false if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev) return false if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
GitalyClient.migrate(:force_push) do |is_enabled| !project
if is_enabled .repository
!project .gitaly_commit_client
.repository .ancestor?(oldrev, newrev)
.gitaly_commit_client
.ancestor?(oldrev, newrev)
else
Gitlab::Git::RevList.new(
project.repository.raw, oldrev: oldrev, newrev: newrev
).missed_ref.present?
end
end
end end
end end
end end
......
...@@ -2,10 +2,10 @@ module Gitlab ...@@ -2,10 +2,10 @@ module Gitlab
class Favicon class Favicon
class << self class << self
def main def main
return appearance_favicon.url if appearance_favicon.exists?
image_name = image_name =
if Gitlab::Utils.to_boolean(ENV['CANARY']) if appearance_favicon.exists?
appearance_favicon.url
elsif Gitlab::Utils.to_boolean(ENV['CANARY'])
'favicon-yellow.png' 'favicon-yellow.png'
elsif Rails.env.development? elsif Rails.env.development?
'favicon-blue.png' 'favicon-blue.png'
...@@ -13,7 +13,7 @@ module Gitlab ...@@ -13,7 +13,7 @@ module Gitlab
'favicon.png' 'favicon.png'
end end
ActionController::Base.helpers.image_path(image_name) ActionController::Base.helpers.image_path(image_name, host: host)
end end
def status_overlay(status_name) def status_overlay(status_name)
...@@ -22,7 +22,7 @@ module Gitlab ...@@ -22,7 +22,7 @@ module Gitlab
"#{status_name}.png" "#{status_name}.png"
) )
ActionController::Base.helpers.image_path(path) ActionController::Base.helpers.image_path(path, host: host)
end end
def available_status_names def available_status_names
...@@ -35,6 +35,16 @@ module Gitlab ...@@ -35,6 +35,16 @@ module Gitlab
private private
# we only want to create full urls when there's a different asset_host
# configured.
def host
if Gitlab::Application.config.asset_host.nil? || Gitlab::Application.config.asset_host == Gitlab.config.gitlab.base_url
nil
else
Gitlab.config.gitlab.base_url
end
end
def appearance def appearance
RequestStore.store[:appearance] ||= (Appearance.current || Appearance.new) RequestStore.store[:appearance] ||= (Appearance.current || Appearance.new)
end end
......
...@@ -14,14 +14,21 @@ module Gitlab ...@@ -14,14 +14,21 @@ module Gitlab
end end
def find(query) def find(query)
by_content = find_by_content(query) query = Gitlab::Search::Query.new(query) do
filter :filename, matcher: ->(filter, blob) { blob.filename =~ /#{filter[:regex_value]}$/i }
filter :path, matcher: ->(filter, blob) { blob.filename =~ /#{filter[:regex_value]}/i }
filter :extension, matcher: ->(filter, blob) { blob.filename =~ /\.#{filter[:regex_value]}$/i }
end
by_content = find_by_content(query.term)
already_found = Set.new(by_content.map(&:filename)) already_found = Set.new(by_content.map(&:filename))
by_filename = find_by_filename(query, except: already_found) by_filename = find_by_filename(query.term, except: already_found)
files = (by_content + by_filename)
.sort_by(&:filename)
(by_content + by_filename) query.filter_results(files).map { |blob| [blob.filename, blob] }
.sort_by(&:filename)
.map { |blob| [blob.filename, blob] }
end end
private private
......
...@@ -7,67 +7,11 @@ module Gitlab ...@@ -7,67 +7,11 @@ module Gitlab
end end
def new_pointers(object_limit: nil, not_in: nil) def new_pointers(object_limit: nil, not_in: nil)
@repository.gitaly_migrate(:blob_get_new_lfs_pointers) do |is_enabled| @repository.gitaly_blob_client.get_new_lfs_pointers(@newrev, object_limit, not_in)
if is_enabled
@repository.gitaly_blob_client.get_new_lfs_pointers(@newrev, object_limit, not_in)
else
git_new_pointers(object_limit, not_in)
end
end
end end
def all_pointers def all_pointers
@repository.gitaly_migrate(:blob_get_all_lfs_pointers) do |is_enabled| @repository.gitaly_blob_client.get_all_lfs_pointers(@newrev)
if is_enabled
@repository.gitaly_blob_client.get_all_lfs_pointers(@newrev)
else
git_all_pointers
end
end
end
private
def git_new_pointers(object_limit, not_in)
@new_pointers ||= begin
rev_list.new_objects(rev_list_params(not_in: not_in)) do |object_ids|
object_ids = object_ids.take(object_limit) if object_limit
Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids)
end
end
end
def git_all_pointers
params = {}
if rev_list_supports_new_options?
params[:options] = ["--filter=blob:limit=#{Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE}"]
end
rev_list.all_objects(rev_list_params(params)) do |object_ids|
Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids)
end
end
def rev_list
Gitlab::Git::RevList.new(@repository, newrev: @newrev)
end
# We're passing the `--in-commit-order` arg to ensure we don't wait
# for git to traverse all commits before returning pointers.
# This is required in order to improve the performance of LFS integrity check
def rev_list_params(params = {})
params[:options] ||= []
params[:options] << "--in-commit-order" if rev_list_supports_new_options?
params[:require_path] = true
params
end
def rev_list_supports_new_options?
return @option_supported if defined?(@option_supported)
@option_supported = Gitlab::Git.version >= Gitlab::VersionInfo.parse('2.16.0')
end end
end end
end end
......
...@@ -472,13 +472,21 @@ module Gitlab ...@@ -472,13 +472,21 @@ module Gitlab
end end
def count_commits(options) def count_commits(options)
count_commits_options = process_count_commits_options(options) options = process_count_commits_options(options.dup)
gitaly_migrate(:count_commits, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| wrapped_gitaly_errors do
if is_enabled if options[:left_right]
count_commits_by_gitaly(count_commits_options) from = options[:from]
to = options[:to]
right_count = gitaly_commit_client
.commit_count("#{from}..#{to}", options)
left_count = gitaly_commit_client
.commit_count("#{to}..#{from}", options)
[left_count, right_count]
else else
count_commits_by_shelling_out(count_commits_options) gitaly_commit_client.commit_count(options[:ref], options)
end end
end end
end end
...@@ -676,15 +684,9 @@ module Gitlab ...@@ -676,15 +684,9 @@ module Gitlab
end end
# Return total commits count accessible from passed ref # Return total commits count accessible from passed ref
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/330
def commit_count(ref) def commit_count(ref)
gitaly_migrate(:commit_count, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| wrapped_gitaly_errors do
if is_enabled gitaly_commit_client.commit_count(ref)
gitaly_commit_client.commit_count(ref)
else
rugged_commit_count(ref)
end
end end
end end
...@@ -984,21 +986,7 @@ module Gitlab ...@@ -984,21 +986,7 @@ module Gitlab
def info_attributes def info_attributes
return @info_attributes if @info_attributes return @info_attributes if @info_attributes
content = content = gitaly_repository_client.info_attributes
gitaly_migrate(:get_info_attributes, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_repository_client.info_attributes
else
attributes_path = File.join(File.expand_path(path), 'info', 'attributes')
if File.exist?(attributes_path)
File.read(attributes_path)
else
""
end
end
end
@info_attributes = AttributesParser.new(content) @info_attributes = AttributesParser.new(content)
end end
...@@ -1054,18 +1042,8 @@ module Gitlab ...@@ -1054,18 +1042,8 @@ module Gitlab
end end
def license_short_name def license_short_name
gitaly_migrate(:license_short_name, wrapped_gitaly_errors do
status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| gitaly_repository_client.license_short_name
if is_enabled
gitaly_repository_client.license_short_name
else
begin
# The licensee gem creates a Rugged object from the path:
# https://github.com/benbalter/licensee/blob/v8.7.0/lib/licensee/projects/git_project.rb
Licensee.license(path).try(:key)
rescue Rugged::Error
end
end
end end
end end
...@@ -1252,12 +1230,8 @@ module Gitlab ...@@ -1252,12 +1230,8 @@ module Gitlab
end end
def rebase_in_progress?(rebase_id) def rebase_in_progress?(rebase_id)
gitaly_migrate(:rebase_in_progress) do |is_enabled| wrapped_gitaly_errors do
if is_enabled gitaly_repository_client.rebase_in_progress?(rebase_id)
gitaly_repository_client.rebase_in_progress?(rebase_id)
else
fresh_worktree?(worktree_path(REBASE_WORKTREE_PREFIX, rebase_id))
end
end end
end end
...@@ -1273,12 +1247,8 @@ module Gitlab ...@@ -1273,12 +1247,8 @@ module Gitlab
end end
def squash_in_progress?(squash_id) def squash_in_progress?(squash_id)
gitaly_migrate(:squash_in_progress, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| wrapped_gitaly_errors do
if is_enabled gitaly_repository_client.squash_in_progress?(squash_id)
gitaly_repository_client.squash_in_progress?(squash_id)
else
fresh_worktree?(worktree_path(SQUASH_WORKTREE_PREFIX, squash_id))
end
end end
end end
...@@ -1443,13 +1413,8 @@ module Gitlab ...@@ -1443,13 +1413,8 @@ module Gitlab
end end
def can_be_merged?(source_sha, target_branch) def can_be_merged?(source_sha, target_branch)
gitaly_migrate(:can_be_merged) do |is_enabled| target_sha = find_branch(target_branch, true).target
if is_enabled !gitaly_conflicts_client(source_sha, target_sha).conflicts?
gitaly_can_be_merged?(source_sha, find_branch(target_branch, true).target)
else
rugged_can_be_merged?(source_sha, target_branch)
end
end
end end
def search_files_by_name(query, ref) def search_files_by_name(query, ref)
...@@ -1514,10 +1479,6 @@ module Gitlab ...@@ -1514,10 +1479,6 @@ module Gitlab
run_git!(args, lazy_block: block) run_git!(args, lazy_block: block)
end end
def missed_ref(oldrev, newrev)
run_git!(['rev-list', '--max-count=1', oldrev, "^#{newrev}"])
end
def with_worktree(worktree_path, branch, sparse_checkout_files: nil, env:) def with_worktree(worktree_path, branch, sparse_checkout_files: nil, env:)
base_args = %w(worktree add --detach) base_args = %w(worktree add --detach)
...@@ -1621,21 +1582,6 @@ module Gitlab ...@@ -1621,21 +1582,6 @@ module Gitlab
end end
end end
# This function is duplicated in Gitaly-Go, don't change it!
# https://gitlab.com/gitlab-org/gitaly/merge_requests/698
def fresh_worktree?(path)
File.exist?(path) && !clean_stuck_worktree(path)
end
# This function is duplicated in Gitaly-Go, don't change it!
# https://gitlab.com/gitlab-org/gitaly/merge_requests/698
def clean_stuck_worktree(path)
return false unless File.mtime(path) < 15.minutes.ago
FileUtils.rm_rf(path)
true
end
# Adding a worktree means checking out the repository. For large repos, # Adding a worktree means checking out the repository. For large repos,
# this can be very expensive, so set up sparse checkout for the worktree # this can be very expensive, so set up sparse checkout for the worktree
# to only check out the files we're interested in. # to only check out the files we're interested in.
...@@ -1902,71 +1848,6 @@ module Gitlab ...@@ -1902,71 +1848,6 @@ module Gitlab
gitaly_repository_client.repository_size gitaly_repository_client.repository_size
end end
def count_commits_by_gitaly(options)
if options[:left_right]
from = options[:from]
to = options[:to]
right_count = gitaly_commit_client
.commit_count("#{from}..#{to}", options)
left_count = gitaly_commit_client
.commit_count("#{to}..#{from}", options)
[left_count, right_count]
else
gitaly_commit_client.commit_count(options[:ref], options)
end
end
def count_commits_by_shelling_out(options)
cmd = count_commits_shelling_command(options)
raw_output, _status = run_git(cmd)
process_count_commits_raw_output(raw_output, options)
end
def count_commits_shelling_command(options)
cmd = %w[rev-list]
cmd << "--after=#{options[:after].iso8601}" if options[:after]
cmd << "--before=#{options[:before].iso8601}" if options[:before]
cmd << "--max-count=#{options[:max_count]}" if options[:max_count]
cmd << "--left-right" if options[:left_right]
cmd << '--count'
cmd << if options[:all]
'--all'
elsif options[:ref]
options[:ref]
else
raise ArgumentError, "Please specify a valid ref or set the 'all' attribute to true"
end
cmd += %W[-- #{options[:path]}] if options[:path].present?
cmd
end
def process_count_commits_raw_output(raw_output, options)
if options[:left_right]
result = raw_output.scan(/\d+/).map(&:to_i)
if result.sum != options[:max_count]
result
else # Reaching max count, right is not accurate
right_option =
process_count_commits_options(options
.except(:left_right, :from, :to)
.merge(ref: options[:to]))
right = count_commits_by_shelling_out(right_option)
[result.first, right] # left should be accurate in the first call
end
else
raw_output.to_i
end
end
def gitaly_ls_files(ref) def gitaly_ls_files(ref)
gitaly_commit_client.ls_files(ref) gitaly_commit_client.ls_files(ref)
end end
...@@ -2346,14 +2227,6 @@ module Gitlab ...@@ -2346,14 +2227,6 @@ module Gitlab
run_git(['fetch', remote_name], env: env).last.zero? run_git(['fetch', remote_name], env: env).last.zero?
end end
def gitaly_can_be_merged?(their_commit, our_commit)
!gitaly_conflicts_client(our_commit, their_commit).conflicts?
end
def rugged_can_be_merged?(their_commit, our_commit)
!rugged.merge_commits(our_commit, their_commit).conflicts?
end
def gitlab_projects_error def gitlab_projects_error
raise CommandError, @gitlab_projects.output raise CommandError, @gitlab_projects.output
end end
...@@ -2393,16 +2266,6 @@ module Gitlab ...@@ -2393,16 +2266,6 @@ module Gitlab
nil nil
end end
def rugged_commit_count(ref)
walker = Rugged::Walker.new(rugged)
walker.sorting(Rugged::SORT_TOPO | Rugged::SORT_REVERSE)
oid = rugged.rev_parse_oid(ref)
walker.push(oid)
walker.count
rescue Rugged::ReferenceError
0
end
def rev_list_param(spec) def rev_list_param(spec)
spec == :all ? ['--all'] : spec spec == :all ? ['--all'] : spec
end end
......
# Gitaly note: JV: will probably be migrated indirectly by migrating the call sites.
module Gitlab module Gitlab
module Git module Git
class RevList class RevList
...@@ -45,13 +43,6 @@ module Gitlab ...@@ -45,13 +43,6 @@ module Gitlab
&lazy_block) &lazy_block)
end end
# This methods returns an array of missed references
#
# Should become obsolete after https://gitlab.com/gitlab-org/gitaly/issues/348.
def missed_ref
repository.missed_ref(oldrev, newrev).split("\n")
end
private private
def execute(args) def execute(args)
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
extend Gitlab::Git::Popen extend Gitlab::Git::Popen
def self.git_version def self.git_version
Gitlab::VersionInfo.parse(popen(%W(#{Gitlab.config.git.bin_path} --version), nil).first) Gitlab::VersionInfo.parse(Gitaly::Server.all.first.git_binary_version)
end end
end end
end end
......
...@@ -196,20 +196,21 @@ module Gitlab ...@@ -196,20 +196,21 @@ module Gitlab
end end
def create_bundle(save_path) def create_bundle(save_path)
request = Gitaly::CreateBundleRequest.new(repository: @gitaly_repo) gitaly_fetch_stream_to_file(
response = GitalyClient.call( save_path,
@storage,
:repository_service,
:create_bundle, :create_bundle,
request, Gitaly::CreateBundleRequest,
timeout: GitalyClient.default_timeout GitalyClient.default_timeout
) )
end
File.open(save_path, 'wb') do |f| def backup_custom_hooks(save_path)
response.each do |message| gitaly_fetch_stream_to_file(
f.write(message.data) save_path,
end :backup_custom_hooks,
end Gitaly::BackupCustomHooksRequest,
GitalyClient.default_timeout
)
end end
def create_from_bundle(bundle_path) def create_from_bundle(bundle_path)
...@@ -309,6 +310,25 @@ module Gitlab ...@@ -309,6 +310,25 @@ module Gitlab
private private
def gitaly_fetch_stream_to_file(save_path, rpc_name, request_class, timeout)
request = request_class.new(repository: @gitaly_repo)
response = GitalyClient.call(
@storage,
:repository_service,
rpc_name,
request,
timeout: timeout
)
File.open(save_path, 'wb') do |f|
response.each do |message|
f.write(message.data)
end
end
# If the file is empty means that we recieved an empty stream, we delete the file
FileUtils.rm(save_path) if File.zero?(save_path)
end
def gitaly_repo_stream_request(file_path, rpc_name, request_class, timeout) def gitaly_repo_stream_request(file_path, rpc_name, request_class, timeout)
request = request_class.new(repository: @gitaly_repo) request = request_class.new(repository: @gitaly_repo)
enum = Enumerator.new do |y| enum = Enumerator.new do |y|
......
module Gitlab module Gitlab
module HealthChecks module HealthChecks
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1218
class FsShardsCheck class FsShardsCheck
extend BaseAbstractCheck extend BaseAbstractCheck
RANDOM_STRING = SecureRandom.hex(1000).freeze RANDOM_STRING = SecureRandom.hex(1000).freeze
......
...@@ -2,11 +2,12 @@ module Gitlab ...@@ -2,11 +2,12 @@ module Gitlab
module Kubernetes module Kubernetes
module Helm module Helm
class InstallCommand < BaseCommand class InstallCommand < BaseCommand
attr_reader :name, :chart, :repository, :values attr_reader :name, :chart, :version, :repository, :values
def initialize(name, chart:, values:, repository: nil) def initialize(name, chart:, values:, version: nil, repository: nil)
@name = name @name = name
@chart = chart @chart = chart
@version = version
@values = values @values = values
@repository = repository @repository = repository
end end
...@@ -39,9 +40,13 @@ module Gitlab ...@@ -39,9 +40,13 @@ module Gitlab
def script_command def script_command
<<~HEREDOC <<~HEREDOC
helm install #{chart} --name #{name} --namespace #{Gitlab::Kubernetes::Helm::NAMESPACE} -f /data/helm/#{name}/config/values.yaml >/dev/null helm install #{chart} --name #{name}#{optional_version_flag} --namespace #{Gitlab::Kubernetes::Helm::NAMESPACE} -f /data/helm/#{name}/config/values.yaml >/dev/null
HEREDOC HEREDOC
end end
def optional_version_flag
" --version #{version}" if version
end
end end
end end
end end
......
module Gitlab
module Search
class ParsedQuery
attr_reader :term, :filters
def initialize(term, filters)
@term = term
@filters = filters
end
def filter_results(results)
filters = @filters.reject { |filter| filter[:matcher].nil? }
return unless filters
results.select do |result|
filters.all? do |filter|
filter[:matcher].call(filter, result)
end
end
end
end
end
end
module Gitlab
module Search
class Query < SimpleDelegator
def initialize(query, filter_opts = {}, &block)
@raw_query = query.dup
@filters = []
@filter_options = { default_parser: :downcase.to_proc }.merge(filter_opts)
self.instance_eval(&block) if block_given?
@query = Gitlab::Search::ParsedQuery.new(*extract_filters)
# set the ParsedQuery as our default delegator thanks to SimpleDelegator
super(@query)
end
private
def filter(name, **attributes)
filter = { parser: @filter_options[:default_parser], name: name }.merge(attributes)
@filters << filter
end
def filter_options(**options)
@filter_options.merge!(options)
end
def extract_filters
fragments = []
filters = @filters.each_with_object([]) do |filter, parsed_filters|
match = @raw_query.split.find { |part| part =~ /\A#{filter[:name]}:/ }
next unless match
input = match.split(':')[1..-1].join
next if input.empty?
filter[:value] = parse_filter(filter, input)
filter[:regex_value] = Regexp.escape(filter[:value]).gsub('\*', '.*?')
fragments << match
parsed_filters << filter
end
query = (@raw_query.split - fragments).join(' ')
[query, filters]
end
def parse_filter(filter, input)
filter[:parser].call(input)
end
end
end
end
...@@ -24,6 +24,7 @@ module Gitlab ...@@ -24,6 +24,7 @@ module Gitlab
address = val['gitaly_address'] address = val['gitaly_address']
end end
# https://gitlab.com/gitlab-org/gitaly/issues/1238
Gitlab::GitalyClient::StorageSettings.allow_disk_access do Gitlab::GitalyClient::StorageSettings.allow_disk_access do
storages << { name: key, path: val.legacy_disk_path } storages << { name: key, path: val.legacy_disk_path }
end end
......
# Gitaly note: JV: two sets of straightforward RPC's. 1 Hard RPC: fork_repository. # Gitaly note: SSH key operations are not part of Gitaly so will never be migrated.
# SSH key operations are not part of Gitaly so will never be migrated.
require 'securerandom' require 'securerandom'
...@@ -153,8 +152,6 @@ module Gitlab ...@@ -153,8 +152,6 @@ module Gitlab
# #
# Ex. # Ex.
# mv_repository("/path/to/storage", "gitlab/gitlab-ci", "randx/gitlab-ci-new") # mv_repository("/path/to/storage", "gitlab/gitlab-ci", "randx/gitlab-ci-new")
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/873
def mv_repository(storage, path, new_path) def mv_repository(storage, path, new_path)
return false if path.empty? || new_path.empty? return false if path.empty? || new_path.empty?
...@@ -169,19 +166,11 @@ module Gitlab ...@@ -169,19 +166,11 @@ module Gitlab
# #
# Ex. # Ex.
# fork_repository("nfs-file06", "gitlab/gitlab-ci", "nfs-file07", "new-namespace/gitlab-ci") # fork_repository("nfs-file06", "gitlab/gitlab-ci", "nfs-file07", "new-namespace/gitlab-ci")
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/817
def fork_repository(forked_from_storage, forked_from_disk_path, forked_to_storage, forked_to_disk_path) def fork_repository(forked_from_storage, forked_from_disk_path, forked_to_storage, forked_to_disk_path)
forked_from_relative_path = "#{forked_from_disk_path}.git" forked_from_relative_path = "#{forked_from_disk_path}.git"
fork_args = [forked_to_storage, "#{forked_to_disk_path}.git"] fork_args = [forked_to_storage, "#{forked_to_disk_path}.git"]
gitaly_migrate(:fork_repository, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| GitalyGitlabProjects.new(forked_from_storage, forked_from_relative_path).fork_repository(*fork_args)
if is_enabled
GitalyGitlabProjects.new(forked_from_storage, forked_from_relative_path).fork_repository(*fork_args)
else
gitlab_projects(forked_from_storage, forked_from_relative_path).fork_repository(*fork_args)
end
end
end end
# Removes a repository from file system, using rm_diretory which is an alias # Removes a repository from file system, using rm_diretory which is an alias
...@@ -193,8 +182,6 @@ module Gitlab ...@@ -193,8 +182,6 @@ module Gitlab
# #
# Ex. # Ex.
# remove_repository("/path/to/storage", "gitlab/gitlab-ci") # remove_repository("/path/to/storage", "gitlab/gitlab-ci")
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/873
def remove_repository(storage, name) def remove_repository(storage, name)
return false if name.empty? return false if name.empty?
......
...@@ -53,25 +53,34 @@ namespace :gettext do ...@@ -53,25 +53,34 @@ namespace :gettext do
task :updated_check do task :updated_check do
# Removing all pre-translated files speeds up `gettext:find` as the # Removing all pre-translated files speeds up `gettext:find` as the
# files don't need to be merged. # files don't need to be merged.
`rm locale/*/gitlab.po` # Having `LC_MESSAGES/gitlab.mo files present also confuses the output.
FileUtils.rm Dir['locale/**/gitlab.*']
# Make sure we start out with a clean pot.file
`git checkout -- locale/gitlab.pot`
# `gettext:find` writes touches to temp files to `stderr` which would cause # `gettext:find` writes touches to temp files to `stderr` which would cause
# `static-analysis` to report failures. We can ignore these # `static-analysis` to report failures. We can ignore these.
silence_stream(STDERR) { Rake::Task['gettext:find'].invoke } silence_stream($stderr) do
Rake::Task['gettext:find'].invoke
end
changed_files = `git diff --name-only`.lines.map(&:strip) pot_diff = `git diff -- locale/gitlab.pot`.strip
# reset the locale folder for potential next tasks # reset the locale folder for potential next tasks
`git checkout -- locale` `git checkout -- locale`
if changed_files.include?('locale/gitlab.pot') if pot_diff.present?
raise <<~MSG raise <<~MSG
Newly translated strings found, please add them to `gitlab.pot` by running: Newly translated strings found, please add them to `gitlab.pot` by running:
bundle exec rake gettext:find; git checkout -- locale/*/gitlab.po; rm locale/**/gitlab.*; bin/rake gettext:find; git checkout -- locale/*/gitlab.po
Then commit and push the resulting changes to `locale/gitlab.pot`. Then commit and push the resulting changes to `locale/gitlab.pot`.
The diff was:
#{pot_diff}
MSG MSG
end end
end end
......
...@@ -17,13 +17,22 @@ unless Rails.env.production? ...@@ -17,13 +17,22 @@ unless Rails.env.production?
Rake::Task['eslint'].invoke Rake::Task['eslint'].invoke
end end
desc "GitLab | lint | Lint HAML files"
task :haml do
begin
Rake::Task['haml_lint'].invoke
rescue RuntimeError # The haml_lint tasks raise a RuntimeError
exit(1)
end
end
desc "GitLab | lint | Run several lint checks" desc "GitLab | lint | Run several lint checks"
task :all do task :all do
status = 0 status = 0
%w[ %w[
config_lint config_lint
haml_lint lint:haml
scss_lint scss_lint
flay flay
gettext:lint gettext:lint
...@@ -39,13 +48,12 @@ unless Rails.env.production? ...@@ -39,13 +48,12 @@ unless Rails.env.production?
$stderr.reopen(wr_err) $stderr.reopen(wr_err)
begin begin
begin Rake::Task[task].invoke
Rake::Task[task].invoke
rescue RuntimeError # The haml_lint tasks raise a RuntimeError
exit(1)
end
rescue SystemExit => ex rescue SystemExit => ex
msg = "*** Rake task #{task} failed with the following error(s):" msg = "*** Rake task #{task} exited:"
raise ex
rescue => ex
msg = "*** Rake task #{task} raised #{ex.class}:"
raise ex raise ex
ensure ensure
$stdout.reopen(stdout) $stdout.reopen(stdout)
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
"dev-server": "nodemon -w 'config/webpack.config.js' --exec 'webpack-dev-server --config config/webpack.config.js'", "dev-server": "nodemon -w 'config/webpack.config.js' --exec 'webpack-dev-server --config config/webpack.config.js'",
"eslint": "eslint --max-warnings 0 --ext .js,.vue .", "eslint": "eslint --max-warnings 0 --ext .js,.vue .",
"eslint-fix": "eslint --max-warnings 0 --ext .js,.vue --fix .", "eslint-fix": "eslint --max-warnings 0 --ext .js,.vue --fix .",
"eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html .", "eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html --no-inline-config .",
"karma": "BABEL_ENV=${BABEL_ENV:=karma} karma start --single-run true config/karma.config.js", "karma": "BABEL_ENV=${BABEL_ENV:=karma} karma start --single-run true config/karma.config.js",
"karma-coverage": "BABEL_ENV=coverage karma start --single-run true config/karma.config.js", "karma-coverage": "BABEL_ENV=coverage karma start --single-run true config/karma.config.js",
"karma-start": "BABEL_ENV=karma karma start config/karma.config.js", "karma-start": "BABEL_ENV=karma karma start config/karma.config.js",
......
...@@ -26,53 +26,58 @@ module QA ...@@ -26,53 +26,58 @@ module QA
end end
def initialize def initialize
# The login page is usually the entry point for all the scenarios so
# we need to wait for the instance to start. That said, in some cases
# we are already logged-in so we check both cases here.
wait(max: 500) do wait(max: 500) do
page.has_css?('.application') page.has_css?('.login-page') ||
Page::Menu::Main.act { has_personal_area? }
end end
end end
def set_initial_password_if_present def sign_in_using_credentials
if page.has_content?('Change your password') # Don't try to log-in if we're already logged-in
fill_in :user_password, with: Runtime::User.password return if Page::Menu::Main.act { has_personal_area? }
fill_in :user_password_confirmation, with: Runtime::User.password
click_button 'Change your password' using_wait_time 0 do
set_initial_password_if_present
if Runtime::User.ldap_user?
sign_in_using_ldap_credentials
else
sign_in_using_gitlab_credentials
end
end end
end end
def sign_in_using_credentials def self.path
if Runtime::User.ldap_user? '/users/sign_in'
sign_in_using_ldap_credentials
else
sign_in_using_gitlab_credentials
end
end end
def sign_in_using_ldap_credentials private
using_wait_time 0 do
set_initial_password_if_present
click_link 'LDAP' def sign_in_using_ldap_credentials
click_link 'LDAP'
fill_in :username, with: Runtime::User.ldap_username fill_in :username, with: Runtime::User.ldap_username
fill_in :password, with: Runtime::User.ldap_password fill_in :password, with: Runtime::User.ldap_password
click_button 'Sign in' click_button 'Sign in'
end
end end
def sign_in_using_gitlab_credentials def sign_in_using_gitlab_credentials
using_wait_time 0 do click_link 'Standard' if page.has_content?('LDAP')
set_initial_password_if_present
click_link 'Standard' if page.has_content?('LDAP')
fill_in :user_login, with: Runtime::User.name fill_in :user_login, with: Runtime::User.name
fill_in :user_password, with: Runtime::User.password fill_in :user_password, with: Runtime::User.password
click_button 'Sign in' click_button 'Sign in'
end
end end
def self.path def set_initial_password_if_present
'/users/sign_in' return unless page.has_content?('Change your password')
fill_in :user_password, with: Runtime::User.password
fill_in :user_password_confirmation, with: Runtime::User.password
click_button 'Change your password'
end end
end end
end end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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